diff options
Diffstat (limited to 'src')
197 files changed, 3979 insertions, 1710 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2ba1da195..32cb85de0 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -32,14 +32,14 @@ add_library(common STATIC break_points.cpp break_points.h chunk_file.h - code_block.h + cityhash.cpp + cityhash.h color.h common_funcs.h common_paths.h common_types.h file_util.cpp file_util.h - hash.cpp hash.h linear_disk_cache.h logging/backend.cpp diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 0cc0a1be0..65e357dec 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -115,7 +115,7 @@ private: // assignment would copy the full storage value, rather than just the bits // relevant to this particular bit field. // We don't delete it because we want BitField to be trivially copyable. - BitField& operator=(const BitField&) = default; + constexpr BitField& operator=(const BitField&) = default; // StorageType is T for non-enum types and the underlying type of T if // T is an enumeration. Note that T is wrapped within an enable_if in the @@ -166,20 +166,20 @@ public: // so that we can use this within unions constexpr BitField() = default; - FORCE_INLINE operator T() const { + constexpr FORCE_INLINE operator T() const { return Value(); } - FORCE_INLINE void Assign(const T& value) { + constexpr FORCE_INLINE void Assign(const T& value) { storage = (storage & ~mask) | FormatValue(value); } - FORCE_INLINE T Value() const { + constexpr T Value() const { return ExtractValue(storage); } // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015 - FORCE_INLINE bool ToBool() const { + constexpr FORCE_INLINE bool ToBool() const { return Value() != 0; } @@ -192,11 +192,6 @@ private: static_assert(position < 8 * sizeof(T), "Invalid position"); static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); static_assert(bits > 0, "Invalid number of bits"); - static_assert(std::is_pod<T>::value, "Invalid base type"); + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); }; #pragma pack() - -#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) -static_assert(std::is_trivially_copyable<BitField<0, 1, unsigned>>::value, - "BitField must be trivially copyable"); -#endif diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp new file mode 100644 index 000000000..de31ffbd8 --- /dev/null +++ b/src/common/cityhash.cpp @@ -0,0 +1,340 @@ +// Copyright (c) 2011 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// This file provides CityHash64() and related functions. +// +// It's probably possible to create even faster hash functions by +// writing a program that systematically explores some of the space of +// possible hash functions, by using SIMD instructions, or by +// compromising on hash quality. + +#include <algorithm> +#include <string.h> // for memcpy and memset +#include "cityhash.h" +#include "common/swap.h" + +// #include "config.h" +#ifdef __GNUC__ +#define HAVE_BUILTIN_EXPECT 1 +#endif +#ifdef COMMON_BIG_ENDIAN +#define WORDS_BIGENDIAN 1 +#endif + +using namespace std; + +typedef uint8_t uint8; +typedef uint32_t uint32; +typedef uint64_t uint64; + +namespace Common { + +static uint64 UNALIGNED_LOAD64(const char* p) { + uint64 result; + memcpy(&result, p, sizeof(result)); + return result; +} + +static uint32 UNALIGNED_LOAD32(const char* p) { + uint32 result; + memcpy(&result, p, sizeof(result)); + return result; +} + +#ifdef WORDS_BIGENDIAN +#define uint32_in_expected_order(x) (swap32(x)) +#define uint64_in_expected_order(x) (swap64(x)) +#else +#define uint32_in_expected_order(x) (x) +#define uint64_in_expected_order(x) (x) +#endif + +#if !defined(LIKELY) +#if HAVE_BUILTIN_EXPECT +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#else +#define LIKELY(x) (x) +#endif +#endif + +static uint64 Fetch64(const char* p) { + return uint64_in_expected_order(UNALIGNED_LOAD64(p)); +} + +static uint32 Fetch32(const char* p) { + return uint32_in_expected_order(UNALIGNED_LOAD32(p)); +} + +// Some primes between 2^63 and 2^64 for various uses. +static const uint64 k0 = 0xc3a5c85c97cb3127ULL; +static const uint64 k1 = 0xb492b66fbe98f273ULL; +static const uint64 k2 = 0x9ae16a3b2f90404fULL; + +// Bitwise right rotate. Normally this will compile to a single +// instruction, especially if the shift is a manifest constant. +static uint64 Rotate(uint64 val, int shift) { + // Avoid shifting by 64: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); +} + +static uint64 ShiftMix(uint64 val) { + return val ^ (val >> 47); +} + +static uint64 HashLen16(uint64 u, uint64 v) { + return Hash128to64(uint128(u, v)); +} + +static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { + // Murmur-inspired hashing. + uint64 a = (u ^ v) * mul; + a ^= (a >> 47); + uint64 b = (v ^ a) * mul; + b ^= (b >> 47); + b *= mul; + return b; +} + +static uint64 HashLen0to16(const char* s, size_t len) { + if (len >= 8) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) + k2; + uint64 b = Fetch64(s + len - 8); + uint64 c = Rotate(b, 37) * mul + a; + uint64 d = (Rotate(a, 25) + b) * mul; + return HashLen16(c, d, mul); + } + if (len >= 4) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch32(s); + return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); + } + if (len > 0) { + uint8 a = s[0]; + uint8 b = s[len >> 1]; + uint8 c = s[len - 1]; + uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8); + uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2); + return ShiftMix(y * k2 ^ z * k0) * k2; + } + return k2; +} + +// This probably works well for 16-byte strings as well, but it may be overkill +// in that case. +static uint64 HashLen17to32(const char* s, size_t len) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) * k1; + uint64 b = Fetch64(s + 8); + uint64 c = Fetch64(s + len - 8) * mul; + uint64 d = Fetch64(s + len - 16) * k2; + return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul); +} + +// Return a 16-byte hash for 48 bytes. Quick and dirty. +// Callers do best to use "random-looking" values for a and b. +static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, + uint64 b) { + a += w; + b = Rotate(b + a + z, 21); + uint64 c = a; + a += x; + a += y; + b += Rotate(a, 44); + return make_pair(a + z, b + c); +} + +// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. +static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) { + return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, + b); +} + +// Return an 8-byte hash for 33 to 64 bytes. +static uint64 HashLen33to64(const char* s, size_t len) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) * k2; + uint64 b = Fetch64(s + 8); + uint64 c = Fetch64(s + len - 24); + uint64 d = Fetch64(s + len - 32); + uint64 e = Fetch64(s + 16) * k2; + uint64 f = Fetch64(s + 24) * 9; + uint64 g = Fetch64(s + len - 8); + uint64 h = Fetch64(s + len - 16) * mul; + uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; + uint64 v = ((a + g) ^ d) + f + 1; + uint64 w = swap64((u + v) * mul) + h; + uint64 x = Rotate(e + f, 42) + c; + uint64 y = (swap64((v + w) * mul) + g) * mul; + uint64 z = e + f + c; + a = swap64((x + z) * mul + y) + b; + b = ShiftMix((z + a) * mul + d + h) * mul; + return b + x; +} + +uint64 CityHash64(const char* s, size_t len) { + if (len <= 32) { + if (len <= 16) { + return HashLen0to16(s, len); + } else { + return HashLen17to32(s, len); + } + } else if (len <= 64) { + return HashLen33to64(s, len); + } + + // For strings over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + uint64 x = Fetch64(s + len - 40); + uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); + uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); + pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z); + pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); + x = x * k1 + Fetch64(s); + + // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. + len = (len - 1) & ~static_cast<size_t>(63); + do { + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + len -= 64; + } while (len != 0); + return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, + HashLen16(v.second, w.second) + x); +} + +uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { + return CityHash64WithSeeds(s, len, k2, seed); +} + +uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) { + return HashLen16(CityHash64(s, len) - seed0, seed1); +} + +// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings +// of any length representable in signed long. Based on City and Murmur. +static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { + uint64 a = Uint128Low64(seed); + uint64 b = Uint128High64(seed); + uint64 c = 0; + uint64 d = 0; + signed long l = static_cast<long>(len) - 16; + if (l <= 0) { // len <= 16 + a = ShiftMix(a * k1) * k1; + c = b * k1 + HashLen0to16(s, len); + d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); + } else { // len > 16 + c = HashLen16(Fetch64(s + len - 8) + k1, a); + d = HashLen16(b + len, c + Fetch64(s + len - 16)); + a += d; + do { + a ^= ShiftMix(Fetch64(s) * k1) * k1; + a *= k1; + b ^= a; + c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; + c *= k1; + d ^= c; + s += 16; + l -= 16; + } while (l > 0); + } + a = HashLen16(a, c); + b = HashLen16(d, b); + return uint128(a ^ b, HashLen16(b, a)); +} + +uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { + if (len < 128) { + return CityMurmur(s, len, seed); + } + + // We expect len >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + pair<uint64, uint64> v, w; + uint64 x = Uint128Low64(seed); + uint64 y = Uint128High64(seed); + uint64 z = len * k1; + v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); + v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); + w.first = Rotate(y + z, 35) * k1 + x; + w.second = Rotate(x + Fetch64(s + 88), 53) * k1; + + // This is the same inner loop as CityHash64(), manually unrolled. + do { + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + len -= 128; + } while (LIKELY(len >= 128)); + x += Rotate(v.first + z, 49) * k0; + y = y * k0 + Rotate(w.second, 37); + z = z * k0 + Rotate(w.first, 27); + w.first *= 9; + v.first *= k0; + // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. + for (size_t tail_done = 0; tail_done < len;) { + tail_done += 32; + y = Rotate(x + y, 42) * k0 + v.second; + w.first += Fetch64(s + len - tail_done + 16); + x = x * k0 + w.first; + z += w.second + Fetch64(s + len - tail_done); + w.second += v.first; + v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); + v.first *= k0; + } + // At this point our 56 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 56-byte-to-8-byte hashes to get a 16-byte final result. + x = HashLen16(x, v.first); + y = HashLen16(y + z, w.first); + return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); +} + +uint128 CityHash128(const char* s, size_t len) { + return len >= 16 + ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) + : CityHash128WithSeed(s, len, uint128(k0, k1)); +} + +} // namespace Common diff --git a/src/common/cityhash.h b/src/common/cityhash.h new file mode 100644 index 000000000..bcebdb150 --- /dev/null +++ b/src/common/cityhash.h @@ -0,0 +1,110 @@ +// Copyright (c) 2011 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// http://code.google.com/p/cityhash/ +// +// This file provides a few functions for hashing strings. All of them are +// high-quality functions in the sense that they pass standard tests such +// as Austin Appleby's SMHasher. They are also fast. +// +// For 64-bit x86 code, on short strings, we don't know of anything faster than +// CityHash64 that is of comparable quality. We believe our nearest competitor +// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash +// tables and most other hashing (excluding cryptography). +// +// For 64-bit x86 code, on long strings, the picture is more complicated. +// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc., +// CityHashCrc128 appears to be faster than all competitors of comparable +// quality. CityHash128 is also good but not quite as fast. We believe our +// nearest competitor is Bob Jenkins' Spooky. We don't have great data for +// other 64-bit CPUs, but for long strings we know that Spooky is slightly +// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example. +// Note that CityHashCrc128 is declared in citycrc.h. +// +// For 32-bit x86 code, we don't know of anything faster than CityHash32 that +// is of comparable quality. We believe our nearest competitor is Murmur3A. +// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.) +// +// Functions in the CityHash family are not suitable for cryptography. +// +// Please see CityHash's README file for more details on our performance +// measurements and so on. +// +// WARNING: This code has been only lightly tested on big-endian platforms! +// It is known to work well on little-endian platforms that have a small penalty +// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. +// It should work on all 32-bit and 64-bit platforms that allow unaligned reads; +// bug reports are welcome. +// +// By the way, for some hash functions, given strings a and b, the hash +// of a+b is easily derived from the hashes of a and b. This property +// doesn't hold for any hash functions in this file. + +#pragma once + +#include <utility> +#include <stdint.h> +#include <stdlib.h> // for size_t. + +namespace Common { + +typedef std::pair<uint64_t, uint64_t> uint128; + +inline uint64_t Uint128Low64(const uint128& x) { + return x.first; +} +inline uint64_t Uint128High64(const uint128& x) { + return x.second; +} + +// Hash function for a byte array. +uint64_t CityHash64(const char* buf, size_t len); + +// Hash function for a byte array. For convenience, a 64-bit seed is also +// hashed into the result. +uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed); + +// Hash function for a byte array. For convenience, two seeds are also +// hashed into the result. +uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1); + +// Hash function for a byte array. +uint128 CityHash128(const char* s, size_t len); + +// Hash function for a byte array. For convenience, a 128-bit seed is also +// hashed into the result. +uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +inline uint64_t Hash128to64(const uint128& x) { + // Murmur-inspired hashing. + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; + a ^= (a >> 47); + uint64_t b = (Uint128High64(x) ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +} // namespace Common diff --git a/src/common/code_block.h b/src/common/code_block.h deleted file mode 100644 index 6a55a8e30..000000000 --- a/src/common/code_block.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include <cstddef> -#include "common/common_types.h" -#include "common/memory_util.h" - -// Everything that needs to generate code should inherit from this. -// You get memory management for free, plus, you can use all emitter functions without -// having to prefix them with gen-> or something similar. -// Example implementation: -// class JIT : public CodeBlock<ARMXEmitter> {} -template <class T> -class CodeBlock : public T, NonCopyable { -private: - // A privately used function to set the executable RAM space to something invalid. - // For debugging usefulness it should be used to set the RAM to a host specific breakpoint - // instruction - virtual void PoisonMemory() = 0; - -protected: - u8* region; - size_t region_size; - -public: - CodeBlock() : region(nullptr), region_size(0) {} - virtual ~CodeBlock() { - if (region) - FreeCodeSpace(); - } - - // Call this before you generate any code. - void AllocCodeSpace(int size) { - region_size = size; - region = (u8*)AllocateExecutableMemory(region_size); - T::SetCodePtr(region); - } - - // Always clear code space with breakpoints, so that if someone accidentally executes - // uninitialized, it just breaks into the debugger. - void ClearCodeSpace() { - PoisonMemory(); - ResetCodePtr(); - } - - // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. - void FreeCodeSpace() { -#ifdef __SYMBIAN32__ - ResetExecutableMemory(region); -#else - FreeMemoryPages(region, region_size); -#endif - region = nullptr; - region_size = 0; - } - - bool IsInSpace(const u8* ptr) { - return (ptr >= region) && (ptr < (region + region_size)); - } - - // Cannot currently be undone. Will write protect the entire code region. - // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). - void WriteProtect() { - WriteProtectMemory(region, region_size, true); - } - - void ResetCodePtr() { - T::SetCodePtr(region); - } - - size_t GetSpaceLeft() const { - return region_size - (T::GetCodePtr() - region); - } - - u8* GetBasePtr() { - return region; - } - - size_t GetOffset(const u8* ptr) const { - return ptr - region; - } -}; diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 6f0604958..7cf7b7997 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -9,8 +9,6 @@ #endif #include "common/common_types.h" -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. #define CONCAT2(x, y) DO_CONCAT2(x, y) #define DO_CONCAT2(x, y) x##y @@ -74,11 +72,6 @@ inline u64 _rotr64(u64 x, unsigned int shift) { #else // _MSC_VER -#if (_MSC_VER < 1900) -// Function Cross-Compatibility -#define snprintf _snprintf -#endif - // Locale Cross-Compatibility #define locale_t _locale_t diff --git a/src/common/common_types.h b/src/common/common_types.h index 844d34965..6b1766dca 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -27,29 +27,23 @@ #include <array> #include <cstdint> -#ifdef _MSC_VER -#ifndef __func__ -#define __func__ __FUNCTION__ -#endif -#endif +using u8 = std::uint8_t; ///< 8-bit unsigned byte +using u16 = std::uint16_t; ///< 16-bit unsigned short +using u32 = std::uint32_t; ///< 32-bit unsigned word +using u64 = std::uint64_t; ///< 64-bit unsigned int -typedef std::uint8_t u8; ///< 8-bit unsigned byte -typedef std::uint16_t u16; ///< 16-bit unsigned short -typedef std::uint32_t u32; ///< 32-bit unsigned word -typedef std::uint64_t u64; ///< 64-bit unsigned int +using s8 = std::int8_t; ///< 8-bit signed byte +using s16 = std::int16_t; ///< 16-bit signed short +using s32 = std::int32_t; ///< 32-bit signed word +using s64 = std::int64_t; ///< 64-bit signed int -typedef std::int8_t s8; ///< 8-bit signed byte -typedef std::int16_t s16; ///< 16-bit signed short -typedef std::int32_t s32; ///< 32-bit signed word -typedef std::int64_t s64; ///< 64-bit signed int - -typedef float f32; ///< 32-bit floating point -typedef double f64; ///< 64-bit floating point +using f32 = float; ///< 32-bit floating point +using f64 = double; ///< 64-bit floating point // TODO: It would be nice to eventually replace these with strong types that prevent accidental // conversion between each other. -typedef u64 VAddr; ///< Represents a pointer in the userspace virtual address space. -typedef u64 PAddr; ///< Represents a pointer in the ARM11 physical address space. +using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. +using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space. using u128 = std::array<std::uint64_t, 2>; static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); diff --git a/src/common/hash.cpp b/src/common/hash.cpp deleted file mode 100644 index a02e9e5b9..000000000 --- a/src/common/hash.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#if defined(_MSC_VER) -#include <stdlib.h> -#endif -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/hash.h" - -namespace Common { - -// MurmurHash3 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -// Block read - if your platform needs to do endian-swapping or can only handle aligned reads, do -// the conversion here -static FORCE_INLINE u64 getblock64(const u64* p, size_t i) { - return p[i]; -} - -// Finalization mix - force all bits of a hash block to avalanche -static FORCE_INLINE u64 fmix64(u64 k) { - k ^= k >> 33; - k *= 0xff51afd7ed558ccdllu; - k ^= k >> 33; - k *= 0xc4ceb9fe1a85ec53llu; - k ^= k >> 33; - - return k; -} - -// This is the 128-bit variant of the MurmurHash3 hash function that is targeted for 64-bit -// platforms (MurmurHash3_x64_128). It was taken from: -// https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp -void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out) { - const u8* data = (const u8*)key; - const size_t nblocks = len / 16; - - u64 h1 = seed; - u64 h2 = seed; - - const u64 c1 = 0x87c37b91114253d5llu; - const u64 c2 = 0x4cf5ad432745937fllu; - - // Body - - const u64* blocks = (const u64*)(data); - - for (size_t i = 0; i < nblocks; i++) { - u64 k1 = getblock64(blocks, i * 2 + 0); - u64 k2 = getblock64(blocks, i * 2 + 1); - - k1 *= c1; - k1 = _rotl64(k1, 31); - k1 *= c2; - h1 ^= k1; - - h1 = _rotl64(h1, 27); - h1 += h2; - h1 = h1 * 5 + 0x52dce729; - - k2 *= c2; - k2 = _rotl64(k2, 33); - k2 *= c1; - h2 ^= k2; - - h2 = _rotl64(h2, 31); - h2 += h1; - h2 = h2 * 5 + 0x38495ab5; - } - - // Tail - - const u8* tail = (const u8*)(data + nblocks * 16); - - u64 k1 = 0; - u64 k2 = 0; - - switch (len & 15) { - case 15: - k2 ^= ((u64)tail[14]) << 48; - case 14: - k2 ^= ((u64)tail[13]) << 40; - case 13: - k2 ^= ((u64)tail[12]) << 32; - case 12: - k2 ^= ((u64)tail[11]) << 24; - case 11: - k2 ^= ((u64)tail[10]) << 16; - case 10: - k2 ^= ((u64)tail[9]) << 8; - case 9: - k2 ^= ((u64)tail[8]) << 0; - k2 *= c2; - k2 = _rotl64(k2, 33); - k2 *= c1; - h2 ^= k2; - - case 8: - k1 ^= ((u64)tail[7]) << 56; - case 7: - k1 ^= ((u64)tail[6]) << 48; - case 6: - k1 ^= ((u64)tail[5]) << 40; - case 5: - k1 ^= ((u64)tail[4]) << 32; - case 4: - k1 ^= ((u64)tail[3]) << 24; - case 3: - k1 ^= ((u64)tail[2]) << 16; - case 2: - k1 ^= ((u64)tail[1]) << 8; - case 1: - k1 ^= ((u64)tail[0]) << 0; - k1 *= c1; - k1 = _rotl64(k1, 31); - k1 *= c2; - h1 ^= k1; - }; - - // Finalization - - h1 ^= len; - h2 ^= len; - - h1 += h2; - h2 += h1; - - h1 = fmix64(h1); - h2 = fmix64(h2); - - h1 += h2; - h2 += h1; - - ((u64*)out)[0] = h1; - ((u64*)out)[1] = h2; -} - -} // namespace Common diff --git a/src/common/hash.h b/src/common/hash.h index ee2560dad..73c326980 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -5,12 +5,12 @@ #pragma once #include <cstddef> +#include <cstring> +#include "common/cityhash.h" #include "common/common_types.h" namespace Common { -void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out); - /** * Computes a 64-bit hash over the specified block of data * @param data Block of data to compute hash over @@ -18,9 +18,54 @@ void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out); * @returns 64-bit hash value that was computed over the data block */ static inline u64 ComputeHash64(const void* data, size_t len) { - u64 res[2]; - MurmurHash3_128(data, len, 0, res); - return res[0]; + return CityHash64(static_cast<const char*>(data), len); +} + +/** + * Computes a 64-bit hash of a struct. In addition to being trivially copyable, it is also critical + * that either the struct includes no padding, or that any padding is initialized to a known value + * by memsetting the struct to 0 before filling it in. + */ +template <typename T> +static inline u64 ComputeStructHash64(const T& data) { + static_assert(std::is_trivially_copyable<T>(), + "Type passed to ComputeStructHash64 must be trivially copyable"); + return ComputeHash64(&data, sizeof(data)); } +/// A helper template that ensures the padding in a struct is initialized by memsetting to 0. +template <typename T> +struct HashableStruct { + // In addition to being trivially copyable, T must also have a trivial default constructor, + // because any member initialization would be overridden by memset + static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial"); + /* + * We use a union because "implicitly-defined copy/move constructor for a union X copies the + * object representation of X." and "implicitly-defined copy assignment operator for a union X + * copies the object representation (3.9) of X." = Bytewise copy instead of memberwise copy. + * This is important because the padding bytes are included in the hash and comparison between + * objects. + */ + union { + T state; + }; + + HashableStruct() { + // Memset structure to zero padding bits, so that they will be deterministic when hashing + std::memset(&state, 0, sizeof(T)); + } + + bool operator==(const HashableStruct<T>& o) const { + return std::memcmp(&state, &o.state, sizeof(T)) == 0; + }; + + bool operator!=(const HashableStruct<T>& o) const { + return !(*this == o); + }; + + size_t Hash() const { + return Common::ComputeStructHash64(state); + } +}; + } // namespace Common diff --git a/src/common/math_util.h b/src/common/math_util.h index 45a1ed367..c6a83c953 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -17,11 +17,6 @@ inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1)); } -template <typename T> -inline T Clamp(const T val, const T& min, const T& max) { - return std::max(min, std::min(max, val)); -} - template <class T> struct Rectangle { T left; diff --git a/src/common/thread.h b/src/common/thread.h index fa475ab51..9465e1de7 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -11,25 +11,6 @@ #include <thread> #include "common/common_types.h" -// Support for C++11's thread_local keyword was surprisingly spotty in compilers until very -// recently. Fortunately, thread local variables have been well supported for compilers for a while, -// but with semantics supporting only POD types, so we can use a few defines to get some amount of -// backwards compat support. -// WARNING: This only works correctly with POD types. -#if defined(__clang__) -#if !__has_feature(cxx_thread_local) -#define thread_local __thread -#endif -#elif defined(__GNUC__) -#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -#define thread_local __thread -#endif -#elif defined(_MSC_VER) -#if _MSC_VER < 1900 -#define thread_local __declspec(thread) -#endif -#endif - namespace Common { int CurrentThreadId(); diff --git a/src/common/vector_math.h b/src/common/vector_math.h index 3f0057d9e..3f15ac1f4 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -55,10 +55,6 @@ public: T x; T y; - T* AsArray() { - return &x; - } - Vec2() = default; Vec2(const T& _x, const T& _y) : x(_x), y(_y) {} @@ -71,11 +67,6 @@ public: return Vec2<T>(f, f); } - void Write(T a[2]) { - a[0] = x; - a[1] = y; - } - Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { return MakeVec(x + other.x, y + other.y); } @@ -205,10 +196,6 @@ public: T y; T z; - T* AsArray() { - return &x; - } - Vec3() = default; Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {} @@ -225,12 +212,6 @@ public: return MakeVec(f, f, f); } - void Write(T a[3]) { - a[0] = x; - a[1] = y; - a[2] = z; - } - Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { return MakeVec(x + other.x, y + other.y, z + other.z); } @@ -416,10 +397,6 @@ public: T z; T w; - T* AsArray() { - return &x; - } - Vec4() = default; Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {} @@ -436,13 +413,6 @@ public: return Vec4<T>(f, f, f, f); } - void Write(T a[4]) { - a[0] = x; - a[1] = y; - a[2] = z; - a[3] = w; - } - Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9877b83fe..c1a645460 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -12,6 +12,8 @@ add_library(core STATIC file_sys/errors.h file_sys/filesystem.cpp file_sys/filesystem.h + file_sys/partition_filesystem.cpp + file_sys/partition_filesystem.h file_sys/path_parser.cpp file_sys/path_parser.h file_sys/program_metadata.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 9f5507a65..ee4af4dcc 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -12,10 +12,13 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" +#include "core/hle/service/sm/controller.h" +#include "core/hle/service/sm/sm.h" #include "core/hw/hw.h" #include "core/loader/loader.h" #include "core/memory_setup.h" @@ -26,6 +29,8 @@ namespace Core { /*static*/ System System::s_instance; +System::~System() = default; + System::ResultStatus System::RunLoop(bool tight_loop) { status = ResultStatus::Success; if (!cpu_core) { @@ -167,10 +172,12 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { telemetry_session = std::make_unique<Core::TelemetrySession>(); + service_manager = std::make_shared<Service::SM::ServiceManager>(); + HW::Init(); Kernel::Init(system_mode); scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get()); - Service::Init(); + Service::Init(service_manager); GDBStub::Init(); if (!VideoCore::Init(emu_window)) { @@ -200,17 +207,26 @@ void System::Shutdown() { VideoCore::Shutdown(); GDBStub::Shutdown(); Service::Shutdown(); - scheduler = nullptr; + scheduler.reset(); Kernel::Shutdown(); HW::Shutdown(); - telemetry_session = nullptr; - gpu_core = nullptr; - cpu_core = nullptr; + service_manager.reset(); + telemetry_session.reset(); + gpu_core.reset(); + cpu_core.reset(); CoreTiming::Shutdown(); - app_loader = nullptr; + app_loader.reset(); LOG_DEBUG(Core, "Shutdown OK"); } +Service::SM::ServiceManager& System::ServiceManager() { + return *service_manager; +} + +const Service::SM::ServiceManager& System::ServiceManager() const { + return *service_manager; +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index f497dc022..f81cbfb3c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -19,10 +19,16 @@ class EmuWindow; class ARM_Interface; +namespace Service::SM { +class ServiceManager; +} + namespace Core { class System { public: + ~System(); + /** * Gets the instance of the System singleton class. * @returns Reference to the instance of the System singleton class. @@ -137,6 +143,9 @@ public: return *app_loader; } + Service::SM::ServiceManager& ServiceManager(); + const Service::SM::ServiceManager& ServiceManager() const; + void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { debug_context = std::move(context); } @@ -171,6 +180,9 @@ private: /// When true, signals that a reschedule should happen bool reschedule_pending{}; + /// Service manager + std::shared_ptr<Service::SM::ServiceManager> service_manager; + /// Telemetry session for this emulation session std::unique_ptr<Core::TelemetrySession> telemetry_session; diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp index 4235f3935..ca1323873 100644 --- a/src/core/file_sys/disk_filesystem.cpp +++ b/src/core/file_sys/disk_filesystem.cpp @@ -57,10 +57,14 @@ ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std:: std::make_unique<Disk_Storage>(std::move(file))); } -ResultCode Disk_FileSystem::DeleteFile(const Path& path) const { - LOG_WARNING(Service_FS, "(STUBBED) called"); - // TODO(bunnei): Use correct error code - return ResultCode(-1); +ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { + if (!FileUtil::Exists(path)) { + return ERROR_PATH_NOT_FOUND; + } + + FileUtil::Delete(path); + + return RESULT_SUCCESS; } ResultCode Disk_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const { @@ -179,7 +183,7 @@ bool Disk_Storage::SetSize(const u64 size) const { return true; } -Disk_Directory::Disk_Directory(const std::string& path) : directory() { +Disk_Directory::Disk_Directory(const std::string& path) { unsigned size = FileUtil::ScanDirectoryTree(path, directory); directory.size = size; directory.isDirectory = true; diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h index 742d7db1a..8f9e1145a 100644 --- a/src/core/file_sys/disk_filesystem.h +++ b/src/core/file_sys/disk_filesystem.h @@ -25,7 +25,7 @@ public: ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, Mode mode) const override; - ResultCode DeleteFile(const Path& path) const override; + ResultCode DeleteFile(const std::string& path) const override; ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; ResultCode DeleteDirectory(const Path& path) const override; ResultCode DeleteDirectoryRecursively(const Path& path) const override; @@ -43,7 +43,7 @@ protected: class Disk_Storage : public StorageBackend { public: - Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {} + explicit Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {} ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; @@ -60,7 +60,7 @@ private: class Disk_Directory : public DirectoryBackend { public: - Disk_Directory(const std::string& path); + explicit Disk_Directory(const std::string& path); ~Disk_Directory() override { Close(); @@ -74,7 +74,6 @@ public: } protected: - u32 total_entries_in_directory; FileUtil::FSTEntry directory; // We need to remember the last entry we returned, so a subsequent call to Read will continue diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h index 399427ca2..beefcfdb2 100644 --- a/src/core/file_sys/filesystem.h +++ b/src/core/file_sys/filesystem.h @@ -97,7 +97,7 @@ public: * @param path Path relative to the archive * @return Result of the operation */ - virtual ResultCode DeleteFile(const Path& path) const = 0; + virtual ResultCode DeleteFile(const std::string& path) const = 0; /** * Create a directory specified by its path diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp new file mode 100644 index 000000000..4a58a9291 --- /dev/null +++ b/src/core/file_sys/partition_filesystem.cpp @@ -0,0 +1,125 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include <utility> +#include "common/file_util.h" +#include "common/logging/log.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/loader/loader.h" + +namespace FileSys { + +Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { + FileUtil::IOFile file(file_path, "rb"); + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + // At least be as large as the header + if (file.GetSize() < sizeof(Header)) + return Loader::ResultStatus::Error; + + // For cartridges, HFSs can get very large, so we need to calculate the size up to + // the actual content itself instead of just blindly reading in the entire file. + Header pfs_header; + if (!file.ReadBytes(&pfs_header, sizeof(Header))) + return Loader::ResultStatus::Error; + + bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); + size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); + size_t metadata_size = + sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; + + // Actually read in now... + file.Seek(offset, SEEK_SET); + std::vector<u8> file_data(metadata_size); + + if (!file.ReadBytes(file_data.data(), metadata_size)) + return Loader::ResultStatus::Error; + + Loader::ResultStatus result = Load(file_data); + if (result != Loader::ResultStatus::Success) + LOG_ERROR(Service_FS, "Failed to load PFS from file %s!", file_path.c_str()); + + return result; +} + +Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) { + size_t total_size = file_data.size() - offset; + if (total_size < sizeof(Header)) + return Loader::ResultStatus::Error; + + memcpy(&pfs_header, &file_data[offset], sizeof(Header)); + is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); + + size_t entries_offset = offset + sizeof(Header); + size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); + size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); + for (u16 i = 0; i < pfs_header.num_entries; i++) { + FileEntry entry; + + memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); + entry.name = std::string(reinterpret_cast<const char*>( + &file_data[strtab_offset + entry.fs_entry.strtab_offset])); + pfs_entries.push_back(std::move(entry)); + } + + content_offset = strtab_offset + pfs_header.strtab_size; + + return Loader::ResultStatus::Success; +} + +u32 PartitionFilesystem::GetNumEntries() const { + return pfs_header.num_entries; +} + +u64 PartitionFilesystem::GetEntryOffset(int index) const { + if (index > GetNumEntries()) + return 0; + + return content_offset + pfs_entries[index].fs_entry.offset; +} + +u64 PartitionFilesystem::GetEntrySize(int index) const { + if (index > GetNumEntries()) + return 0; + + return pfs_entries[index].fs_entry.size; +} + +std::string PartitionFilesystem::GetEntryName(int index) const { + if (index > GetNumEntries()) + return ""; + + return pfs_entries[index].name; +} + +u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { + for (u32 i = 0; i < pfs_header.num_entries; i++) { + if (pfs_entries[i].name == name) + return content_offset + pfs_entries[i].fs_entry.offset; + } + + return 0; +} + +u64 PartitionFilesystem::GetFileSize(const std::string& name) const { + for (u32 i = 0; i < pfs_header.num_entries; i++) { + if (pfs_entries[i].name == name) + return pfs_entries[i].fs_entry.size; + } + + return 0; +} + +void PartitionFilesystem::Print() const { + NGLOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic.data()); + NGLOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); + for (u32 i = 0; i < pfs_header.num_entries; i++) { + NGLOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, + pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, + GetFileOffset(pfs_entries[i].name)); + } +} +} // namespace FileSys diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h new file mode 100644 index 000000000..573c90057 --- /dev/null +++ b/src/core/file_sys/partition_filesystem.h @@ -0,0 +1,87 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include <vector> +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Loader { +enum class ResultStatus; +} + +namespace FileSys { + +/** + * Helper which implements an interface to parse PFS/HFS filesystems. + * Data can either be loaded from a file path or data with an offset into it. + */ +class PartitionFilesystem { +public: + Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); + Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); + + u32 GetNumEntries() const; + u64 GetEntryOffset(int index) const; + u64 GetEntrySize(int index) const; + std::string GetEntryName(int index) const; + u64 GetFileOffset(const std::string& name) const; + u64 GetFileSize(const std::string& name) const; + + void Print() const; + +private: + struct Header { + std::array<char, 4> magic; + u32_le num_entries; + u32_le strtab_size; + INSERT_PADDING_BYTES(0x4); + }; + + static_assert(sizeof(Header) == 0x10, "PFS/HFS header structure size is wrong"); + +#pragma pack(push, 1) + struct FSEntry { + u64_le offset; + u64_le size; + u32_le strtab_offset; + }; + + static_assert(sizeof(FSEntry) == 0x14, "FS entry structure size is wrong"); + + struct PFSEntry { + FSEntry fs_entry; + INSERT_PADDING_BYTES(0x4); + }; + + static_assert(sizeof(PFSEntry) == 0x18, "PFS entry structure size is wrong"); + + struct HFSEntry { + FSEntry fs_entry; + u32_le hash_region_size; + INSERT_PADDING_BYTES(0x8); + std::array<char, 0x20> hash; + }; + + static_assert(sizeof(HFSEntry) == 0x40, "HFS entry structure size is wrong"); + +#pragma pack(pop) + + struct FileEntry { + FSEntry fs_entry; + std::string name; + }; + + Header pfs_header; + bool is_hfs; + size_t content_offset; + + std::vector<FileEntry> pfs_entries; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp index 0c6cc3157..3d77e2d5f 100644 --- a/src/core/file_sys/romfs_filesystem.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp @@ -20,7 +20,7 @@ ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std: std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); } -ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const { +ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).", GetName().c_str()); // TODO(bunnei): Use correct error code diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h index 3f94c04d0..1b5cac409 100644 --- a/src/core/file_sys/romfs_filesystem.h +++ b/src/core/file_sys/romfs_filesystem.h @@ -31,7 +31,7 @@ public: ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, Mode mode) const override; - ResultCode DeleteFile(const Path& path) const override; + ResultCode DeleteFile(const std::string& path) const override; ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; ResultCode DeleteDirectory(const Path& path) const override; ResultCode DeleteDirectoryRecursively(const Path& path) const override; diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 0149a3ed6..88ca8ad7e 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -34,57 +34,57 @@ SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory cat } } -s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { +s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { switch (resource) { - case COMMIT: + case ResourceType::Commit: return current_commit; - case THREAD: + case ResourceType::Thread: return current_threads; - case EVENT: + case ResourceType::Event: return current_events; - case MUTEX: + case ResourceType::Mutex: return current_mutexes; - case SEMAPHORE: + case ResourceType::Semaphore: return current_semaphores; - case TIMER: + case ResourceType::Timer: return current_timers; - case SHARED_MEMORY: + case ResourceType::SharedMemory: return current_shared_mems; - case ADDRESS_ARBITER: + case ResourceType::AddressArbiter: return current_address_arbiters; - case CPU_TIME: + case ResourceType::CPUTime: return current_cpu_time; default: - LOG_ERROR(Kernel, "Unknown resource type=%08X", resource); + LOG_ERROR(Kernel, "Unknown resource type=%08X", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } } -u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { +u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { switch (resource) { - case PRIORITY: + case ResourceType::Priority: return max_priority; - case COMMIT: + case ResourceType::Commit: return max_commit; - case THREAD: + case ResourceType::Thread: return max_threads; - case EVENT: + case ResourceType::Event: return max_events; - case MUTEX: + case ResourceType::Mutex: return max_mutexes; - case SEMAPHORE: + case ResourceType::Semaphore: return max_semaphores; - case TIMER: + case ResourceType::Timer: return max_timers; - case SHARED_MEMORY: + case ResourceType::SharedMemory: return max_shared_mems; - case ADDRESS_ARBITER: + case ResourceType::AddressArbiter: return max_address_arbiters; - case CPU_TIME: + case ResourceType::CPUTime: return max_cpu_time; default: - LOG_ERROR(Kernel, "Unknown resource type=%08X", resource); + LOG_ERROR(Kernel, "Unknown resource type=%08X", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 1a0ca11f1..cc689a27a 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -16,17 +16,17 @@ enum class ResourceLimitCategory : u8 { OTHER = 3 }; -enum ResourceTypes { - PRIORITY = 0, - COMMIT = 1, - THREAD = 2, - EVENT = 3, - MUTEX = 4, - SEMAPHORE = 5, - TIMER = 6, - SHARED_MEMORY = 7, - ADDRESS_ARBITER = 8, - CPU_TIME = 9, +enum class ResourceType { + Priority = 0, + Commit = 1, + Thread = 2, + Event = 3, + Mutex = 4, + Semaphore = 5, + Timer = 6, + SharedMemory = 7, + AddressArbiter = 8, + CPUTime = 9, }; class ResourceLimit final : public Object { @@ -60,14 +60,14 @@ public: * @param resource Requested resource type * @returns The current value of the resource type */ - s32 GetCurrentResourceValue(u32 resource) const; + s32 GetCurrentResourceValue(ResourceType resource) const; /** * Gets the max value for the specified resource. * @param resource Requested resource type * @returns The max value of the resource type */ - u32 GetMaxResourceValue(u32 resource) const; + u32 GetMaxResourceValue(ResourceType resource) const; /// Name of resource limit object. std::string name; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 36ea23cd9..633740992 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <cinttypes> +#include <iterator> #include "common/logging/log.h" #include "common/microprofile.h" @@ -406,7 +407,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } @@ -540,7 +541,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V } SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } @@ -861,14 +862,14 @@ static const FunctionDef SVC_Table[] = { {0x2B, nullptr, "FlushDataCache"}, {0x2C, nullptr, "MapPhysicalMemory"}, {0x2D, nullptr, "UnmapPhysicalMemory"}, - {0x2E, nullptr, "Unknown"}, + {0x2E, nullptr, "GetNextThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, {0x30, nullptr, "GetResourceLimitLimitValue"}, {0x31, nullptr, "GetResourceLimitCurrentValue"}, {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"}, {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, - {0x34, nullptr, "Unknown"}, - {0x35, nullptr, "Unknown"}, + {0x34, nullptr, "WaitForAddress"}, + {0x35, nullptr, "SignalToAddress"}, {0x36, nullptr, "Unknown"}, {0x37, nullptr, "Unknown"}, {0x38, nullptr, "Unknown"}, @@ -876,7 +877,7 @@ static const FunctionDef SVC_Table[] = { {0x3A, nullptr, "Unknown"}, {0x3B, nullptr, "Unknown"}, {0x3C, nullptr, "DumpInfo"}, - {0x3D, nullptr, "Unknown"}, + {0x3D, nullptr, "DumpInfoNew"}, {0x3E, nullptr, "Unknown"}, {0x3F, nullptr, "Unknown"}, {0x40, nullptr, "CreateSession"}, @@ -887,9 +888,9 @@ static const FunctionDef SVC_Table[] = { {0x45, nullptr, "CreateEvent"}, {0x46, nullptr, "Unknown"}, {0x47, nullptr, "Unknown"}, - {0x48, nullptr, "Unknown"}, - {0x49, nullptr, "Unknown"}, - {0x4A, nullptr, "Unknown"}, + {0x48, nullptr, "AllocateUnsafeMemory"}, + {0x49, nullptr, "FreeUnsafeMemory"}, + {0x4A, nullptr, "SetUnsafeAllocationLimit"}, {0x4B, nullptr, "CreateJitMemory"}, {0x4C, nullptr, "MapJitMemory"}, {0x4D, nullptr, "SleepSystem"}, @@ -926,7 +927,7 @@ static const FunctionDef SVC_Table[] = { {0x6C, nullptr, "SetHardwareBreakPoint"}, {0x6D, nullptr, "GetDebugThreadParam"}, {0x6E, nullptr, "Unknown"}, - {0x6F, nullptr, "Unknown"}, + {0x6F, nullptr, "GetMemoryInfo"}, {0x70, nullptr, "CreatePort"}, {0x71, nullptr, "ManageNamedPort"}, {0x72, nullptr, "ConnectToPort"}, @@ -946,7 +947,7 @@ static const FunctionDef SVC_Table[] = { }; static const FunctionDef* GetSVCInfo(u32 func_num) { - if (func_num >= ARRAY_SIZE(SVC_Table)) { + if (func_num >= std::size(SVC_Table)) { LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); return nullptr; } diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 1c2f873aa..acd65ee68 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -380,7 +380,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { u64 VMManager::GetTotalMemoryUsage() { LOG_WARNING(Kernel, "(STUBBED) called"); - return 0xBE000000; + return 0xF8000000; } u64 VMManager::GetTotalHeapUsage() { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index cfb6e05a5..6bafb2dce 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -10,8 +10,7 @@ #include "core/hle/service/acc/acc_u0.h" #include "core/hle/service/acc/acc_u1.h" -namespace Service { -namespace Account { +namespace Service::Account { // TODO: RE this structure struct UserData { @@ -38,7 +37,10 @@ class IProfile final : public ServiceFramework<IProfile> { public: IProfile() : ServiceFramework("IProfile") { static const FunctionInfo functions[] = { + {0, nullptr, "Get"}, {1, &IProfile::GetBase, "GetBase"}, + {10, nullptr, "GetImageSize"}, + {11, nullptr, "LoadImage"}, }; RegisterHandlers(functions); } @@ -59,6 +61,11 @@ public: static const FunctionInfo functions[] = { {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, + {2, nullptr, "EnsureIdTokenCacheAsync"}, + {3, nullptr, "LoadIdTokenCache"}, + {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, + {150, nullptr, "CreateAuthorizationRequest"}, + {160, nullptr, "StoreOpenContext"}, }; RegisterHandlers(functions); } @@ -140,5 +147,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<ACC_U1>(module)->InstallAsService(service_manager); } -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 2d2f57b7d..58f8d260c 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Account { +namespace Service::Account { class Module final { public: @@ -31,5 +30,4 @@ public: /// Registers all ACC services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index 76deaa07f..280b3e464 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/acc/acc_aa.h" -namespace Service { -namespace Account { +namespace Service::Account { ACC_AA::ACC_AA(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:aa") { static const FunctionInfo functions[] = { @@ -18,5 +17,4 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h index 5069c6890..796f7ef85 100644 --- a/src/core/hle/service/acc/acc_aa.h +++ b/src/core/hle/service/acc/acc_aa.h @@ -6,13 +6,11 @@ #include "core/hle/service/acc/acc.h" -namespace Service { -namespace Account { +namespace Service::Account { class ACC_AA final : public Module::Interface { public: explicit ACC_AA(std::shared_ptr<Module> module); }; -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 538f9d9b1..9ffb40b22 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/acc/acc_su.h" -namespace Service { -namespace Account { +namespace Service::Account { ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") { static const FunctionInfo functions[] = { @@ -51,5 +50,4 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 7b9c667ef..44e21ac09 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/acc/acc_u0.h" -namespace Service { -namespace Account { +namespace Service::Account { ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") { static const FunctionInfo functions[] = { @@ -31,5 +30,4 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index d4f36e172..6ded596b3 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -6,13 +6,11 @@ #include "core/hle/service/acc/acc.h" -namespace Service { -namespace Account { +namespace Service::Account { class ACC_U0 final : public Module::Interface { public: explicit ACC_U0(std::shared_ptr<Module> module); }; -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index dea353554..d101d4e0d 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/acc/acc_u1.h" -namespace Service { -namespace Account { +namespace Service::Account { ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") { static const FunctionInfo functions[] = { @@ -38,5 +37,4 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h index 432d5b3e9..5e3e7659b 100644 --- a/src/core/hle/service/acc/acc_u1.h +++ b/src/core/hle/service/acc/acc_u1.h @@ -6,13 +6,11 @@ #include "core/hle/service/acc/acc.h" -namespace Service { -namespace Account { +namespace Service::Account { class ACC_U1 final : public Module::Interface { public: explicit ACC_U1(std::shared_ptr<Module> module); }; -} // namespace Account -} // namespace Service +} // namespace Service::Account diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index bfc431e88..f41a59afe 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -14,8 +14,7 @@ #include "core/hle/service/nvflinger/nvflinger.h" #include "core/settings.h" -namespace Service { -namespace AM { +namespace Service::AM { IWindowController::IWindowController() : ServiceFramework("IWindowController") { static const FunctionInfo functions[] = { @@ -571,5 +570,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager, std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); } -} // namespace AM -} // namespace Service +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 154d346d5..4f0698a8a 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -8,8 +8,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/nvflinger/nvflinger.h" -namespace Service { -namespace AM { +namespace Service::AM { class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { public: @@ -109,5 +108,4 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) RegisterHandlers(functions); } -} // namespace AM -} // namespace Service +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 334c38392..674b4d753 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -8,8 +8,7 @@ #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/nvflinger/nvflinger.h" -namespace Service { -namespace AM { +namespace Service::AM { class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: @@ -104,5 +103,4 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) RegisterHandlers(functions); } -} // namespace AM -} // namespace Service +} // namespace Service::AM diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index f64001df3..6e7438580 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -6,8 +6,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/aoc/aoc_u.h" -namespace Service { -namespace AOC { +namespace Service::AOC { AOC_U::AOC_U() : ServiceFramework("aoc:u") { static const FunctionInfo functions[] = { @@ -42,5 +41,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AOC_U>()->InstallAsService(service_manager); } -} // namespace AOC -} // namespace Service +} // namespace Service::AOC diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 6e0ba15a5..17d48ef30 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace AOC { +namespace Service::AOC { class AOC_U final : public ServiceFramework<AOC_U> { public: @@ -22,5 +21,4 @@ private: /// Registers all AOC services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace AOC -} // namespace Service +} // namespace Service::AOC diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index c4b09b435..7a185c6c8 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -7,8 +7,7 @@ #include "core/hle/service/apm/apm.h" #include "core/hle/service/apm/interface.h" -namespace Service { -namespace APM { +namespace Service::APM { void InstallInterfaces(SM::ServiceManager& service_manager) { auto module_ = std::make_shared<Module>(); @@ -16,5 +15,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); } -} // namespace APM -} // namespace Service +} // namespace Service::APM diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 070ab21f8..90a80d51b 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace APM { +namespace Service::APM { enum class PerformanceMode : u8 { Handheld = 0, @@ -23,5 +22,4 @@ public: /// Registers all AM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace APM -} // namespace Service +} // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index 0179351ba..4e11f3f14 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -7,8 +7,7 @@ #include "core/hle/service/apm/apm.h" #include "core/hle/service/apm/interface.h" -namespace Service { -namespace APM { +namespace Service::APM { class ISession final : public ServiceFramework<ISession> { public: @@ -62,5 +61,4 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<ISession>(); } -} // namespace APM -} // namespace Service +} // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index 7d53721de..b99dbb412 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace APM { +namespace Service::APM { class APM final : public ServiceFramework<APM> { public: @@ -23,5 +22,4 @@ private: /// Registers all AM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace APM -} // namespace Service +} // namespace Service::APM diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 3c495b3a0..dca2bfb92 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -7,8 +7,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/audin_u.h" -namespace Service { -namespace Audio { +namespace Service::Audio { class IAudioIn final : public ServiceFramework<IAudioIn> { public: @@ -44,5 +43,4 @@ AudInU::AudInU() : ServiceFramework("audin:u") { RegisterHandlers(functions); } -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index 2b8576756..2e65efb5b 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h @@ -10,8 +10,7 @@ namespace Kernel { class HLERequestContext; } -namespace Service { -namespace Audio { +namespace Service::Audio { class AudInU final : public ServiceFramework<AudInU> { public: @@ -19,5 +18,4 @@ public: ~AudInU() = default; }; -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 3f7fb44eb..92f910b5f 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -9,8 +9,7 @@ #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/codecctl.h" -namespace Service { -namespace Audio { +namespace Service::Audio { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AudOutU>()->InstallAsService(service_manager); @@ -20,5 +19,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<CodecCtl>()->InstallAsService(service_manager); } -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index cbd56b2a8..95e5691f7 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h @@ -6,11 +6,9 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Audio { +namespace Service::Audio { /// Registers all Audio services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index db6e6647c..2d7f8cb04 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -10,8 +10,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/audout_u.h" -namespace Service { -namespace Audio { +namespace Service::Audio { /// Switch sample rate frequency constexpr u32 sample_rate{48000}; @@ -204,5 +203,4 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") { RegisterHandlers(functions); } -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 7fbce2225..1f9bb9bcf 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -10,8 +10,7 @@ namespace Kernel { class HLERequestContext; } -namespace Service { -namespace Audio { +namespace Service::Audio { class IAudioOut; @@ -37,5 +36,4 @@ private: }; }; -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index 953104f19..b2be10919 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -7,8 +7,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/audrec_u.h" -namespace Service { -namespace Audio { +namespace Service::Audio { class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> { public: @@ -36,5 +35,4 @@ AudRecU::AudRecU() : ServiceFramework("audrec:u") { RegisterHandlers(functions); } -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h index c31e412c1..46daa33a4 100644 --- a/src/core/hle/service/audio/audrec_u.h +++ b/src/core/hle/service/audio/audrec_u.h @@ -10,8 +10,7 @@ namespace Kernel { class HLERequestContext; } -namespace Service { -namespace Audio { +namespace Service::Audio { class AudRecU final : public ServiceFramework<AudRecU> { public: @@ -19,5 +18,4 @@ public: ~AudRecU() = default; }; -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 0e78c57e9..d9245cb19 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -9,8 +9,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/audren_u.h" -namespace Service { -namespace Audio { +namespace Service::Audio { /// TODO(bunnei): Find a proper value for the audio_ticks constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)}; @@ -272,5 +271,4 @@ void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); } -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index f59d1627d..71b632e80 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -10,8 +10,7 @@ namespace Kernel { class HLERequestContext; } -namespace Service { -namespace Audio { +namespace Service::Audio { class AudRenU final : public ServiceFramework<AudRenU> { public: @@ -24,5 +23,4 @@ private: void GetAudioDevice(Kernel::HLERequestContext& ctx); }; -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp index 1c86d8d17..ba0f1d228 100644 --- a/src/core/hle/service/audio/codecctl.cpp +++ b/src/core/hle/service/audio/codecctl.cpp @@ -7,8 +7,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/codecctl.h" -namespace Service { -namespace Audio { +namespace Service::Audio { CodecCtl::CodecCtl() : ServiceFramework("codecctl") { static const FunctionInfo functions[] = { @@ -29,5 +28,4 @@ CodecCtl::CodecCtl() : ServiceFramework("codecctl") { RegisterHandlers(functions); } -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h index 1121ab0b1..d9ac29b67 100644 --- a/src/core/hle/service/audio/codecctl.h +++ b/src/core/hle/service/audio/codecctl.h @@ -10,8 +10,7 @@ namespace Kernel { class HLERequestContext; } -namespace Service { -namespace Audio { +namespace Service::Audio { class CodecCtl final : public ServiceFramework<CodecCtl> { public: @@ -19,5 +18,4 @@ public: ~CodecCtl() = default; }; -} // namespace Audio -} // namespace Service +} // namespace Service::Audio diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 1a18e0051..41d58f999 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -8,8 +8,7 @@ #include "core/hle/service/fatal/fatal_p.h" #include "core/hle/service/fatal/fatal_u.h" -namespace Service { -namespace Fatal { +namespace Service::Fatal { Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) : ServiceFramework(name), module(std::move(module)) {} @@ -34,5 +33,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); } -} // namespace Fatal -} // namespace Service +} // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 85272b4be..2d8d08320 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Fatal { +namespace Service::Fatal { class Module final { public: @@ -25,5 +24,4 @@ public: void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Fatal -} // namespace Service +} // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index ba194e340..a5254ac2f 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -4,11 +4,9 @@ #include "core/hle/service/fatal/fatal_p.h" -namespace Service { -namespace Fatal { +namespace Service::Fatal { Fatal_P::Fatal_P(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:p") {} -} // namespace Fatal -} // namespace Service +} // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index d77b24bc4..bfd8c8b74 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h @@ -6,13 +6,11 @@ #include "core/hle/service/fatal/fatal.h" -namespace Service { -namespace Fatal { +namespace Service::Fatal { class Fatal_P final : public Module::Interface { public: explicit Fatal_P(std::shared_ptr<Module> module); }; -} // namespace Fatal -} // namespace Service +} // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index 065cc868d..26aa9f3b7 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/fatal/fatal_u.h" -namespace Service { -namespace Fatal { +namespace Service::Fatal { Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { static const FunctionInfo functions[] = { @@ -15,5 +14,4 @@ Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(m RegisterHandlers(functions); } -} // namespace Fatal -} // namespace Service +} // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 22374755e..9b1a9e97a 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h @@ -6,13 +6,11 @@ #include "core/hle/service/fatal/fatal.h" -namespace Service { -namespace Fatal { +namespace Service::Fatal { class Fatal_U final : public Module::Interface { public: explicit Fatal_U(std::shared_ptr<Module> module); }; -} // namespace Fatal -} // namespace Service +} // namespace Service::Fatal diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 945832e98..9e504992f 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -10,8 +10,7 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" -namespace Service { -namespace FileSystem { +namespace Service::FileSystem { /** * Map of registered file systems, identified by type. Once an file system is registered here, it @@ -75,5 +74,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<FSP_SRV>()->InstallAsService(service_manager); } -} // namespace FileSystem -} // namespace Service +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 48c45b1b4..2f476c869 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -14,8 +14,7 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" -namespace Service { -namespace FileSystem { +namespace Service::FileSystem { class IStorage final : public ServiceFramework<IStorage> { public: @@ -73,7 +72,7 @@ public: : ServiceFramework("IFile"), backend(std::move(backend)) { static const FunctionInfo functions[] = { {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, - {2, nullptr, "Flush"}, {3, &IFile::SetSize, "SetSize"}, + {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, {4, &IFile::GetSize, "GetSize"}, {5, nullptr, "OperateRange"}, }; RegisterHandlers(functions); @@ -152,6 +151,14 @@ private: rb.Push(RESULT_SUCCESS); } + void Flush(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + backend->Flush(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void SetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 size = rp.Pop<u64>(); @@ -228,7 +235,7 @@ public: : ServiceFramework("IFileSystem"), backend(std::move(backend)) { static const FunctionInfo functions[] = { {0, &IFileSystem::CreateFile, "CreateFile"}, - {1, nullptr, "DeleteFile"}, + {1, &IFileSystem::DeleteFile, "DeleteFile"}, {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, {3, nullptr, "DeleteDirectory"}, {4, nullptr, "DeleteDirectoryRecursively"}, @@ -265,6 +272,20 @@ public: rb.Push(backend->CreateFile(name, size)); } + void DeleteFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + LOG_DEBUG(Service_FS, "called file %s", name.c_str()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend->DeleteFile(name)); + } + void CreateDirectory(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -551,5 +572,4 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { OpenDataStorageByCurrentProcess(ctx); } -} // namespace FileSystem -} // namespace Service +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 6dc5874c0..acb78fac1 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -11,8 +11,7 @@ namespace FileSys { class FileSystemBackend; } -namespace Service { -namespace FileSystem { +namespace Service::FileSystem { class FSP_SRV final : public ServiceFramework<FSP_SRV> { public: @@ -33,5 +32,4 @@ private: std::unique_ptr<FileSys::FileSystemBackend> romfs; }; -} // namespace FileSystem -} // namespace Service +} // namespace Service::FileSystem diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 051448b2a..c98a46e05 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -8,8 +8,7 @@ #include "core/hle/service/friend/friend_a.h" #include "core/hle/service/friend/friend_u.h" -namespace Service { -namespace Friend { +namespace Service::Friend { void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; @@ -26,5 +25,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<Friend_U>(module)->InstallAsService(service_manager); } -} // namespace Friend -} // namespace Service +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index 2b21b4e15..4b72115c0 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Friend { +namespace Service::Friend { class Module final { public: @@ -25,5 +24,4 @@ public: /// Registers all Friend services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Friend -} // namespace Service +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_a.cpp b/src/core/hle/service/friend/friend_a.cpp index d64fe846a..a2cc81926 100644 --- a/src/core/hle/service/friend/friend_a.cpp +++ b/src/core/hle/service/friend/friend_a.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/friend/friend_a.h" -namespace Service { -namespace Friend { +namespace Service::Friend { Friend_A::Friend_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "friend:a") { @@ -16,5 +15,4 @@ Friend_A::Friend_A(std::shared_ptr<Module> module) RegisterHandlers(functions); } -} // namespace Friend -} // namespace Service +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_a.h b/src/core/hle/service/friend/friend_a.h index 68fa58297..81257583b 100644 --- a/src/core/hle/service/friend/friend_a.h +++ b/src/core/hle/service/friend/friend_a.h @@ -6,13 +6,11 @@ #include "core/hle/service/friend/friend.h" -namespace Service { -namespace Friend { +namespace Service::Friend { class Friend_A final : public Module::Interface { public: explicit Friend_A(std::shared_ptr<Module> module); }; -} // namespace Friend -} // namespace Service +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_u.cpp b/src/core/hle/service/friend/friend_u.cpp index 9a4b05b38..90b30883f 100644 --- a/src/core/hle/service/friend/friend_u.cpp +++ b/src/core/hle/service/friend/friend_u.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/friend/friend_u.h" -namespace Service { -namespace Friend { +namespace Service::Friend { Friend_U::Friend_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "friend:u") { @@ -16,5 +15,4 @@ Friend_U::Friend_U(std::shared_ptr<Module> module) RegisterHandlers(functions); } -} // namespace Friend -} // namespace Service +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_u.h b/src/core/hle/service/friend/friend_u.h index 6be49ff01..0d953d807 100644 --- a/src/core/hle/service/friend/friend_u.h +++ b/src/core/hle/service/friend/friend_u.h @@ -6,13 +6,11 @@ #include "core/hle/service/friend/friend.h" -namespace Service { -namespace Friend { +namespace Service::Friend { class Friend_U final : public Module::Interface { public: explicit Friend_U(std::shared_ptr<Module> module); }; -} // namespace Friend -} // namespace Service +} // namespace Service::Friend diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b59c52f07..aad5e688b 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -14,8 +14,7 @@ #include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" -namespace Service { -namespace HID { +namespace Service::HID { // Updating period for each HID device. // TODO(shinyquagsire23): These need better values. @@ -434,5 +433,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<Hid>()->InstallAsService(service_manager); } -} // namespace HID -} // namespace Service +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 3de9adb4b..350174ccd 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -7,8 +7,7 @@ #include "core/hle/service/service.h" #include "core/settings.h" -namespace Service { -namespace HID { +namespace Service::HID { // Begin enums and output structs @@ -337,5 +336,4 @@ void ReloadInputDevices(); /// Registers all HID services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace HID -} // namespace Service +} // namespace Service::HID diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index b8e53d2c7..b87172dff 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -9,8 +9,7 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/service/lm/lm.h" -namespace Service { -namespace LM { +namespace Service::LM { class Logger final : public ServiceFramework<Logger> { public: @@ -189,5 +188,4 @@ LM::LM() : ServiceFramework("lm") { RegisterHandlers(functions); } -} // namespace LM -} // namespace Service +} // namespace Service::LM diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index 371135057..63d6506fe 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h @@ -8,8 +8,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" -namespace Service { -namespace LM { +namespace Service::LM { class LM final : public ServiceFramework<LM> { public: @@ -23,5 +22,4 @@ private: /// Registers all LM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace LM -} // namespace Service +} // namespace Service::LM diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 49870841c..91e5f527a 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -7,8 +7,7 @@ #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" -namespace Service { -namespace NFP { +namespace Service::NFP { Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) : ServiceFramework(name), module(std::move(module)) {} @@ -24,5 +23,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<NFP_User>(module)->InstallAsService(service_manager); } -} // namespace NFP -} // namespace Service +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 1163e9954..095209ad8 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace NFP { +namespace Service::NFP { class Module final { public: @@ -24,5 +23,4 @@ public: void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace NFP -} // namespace Service +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 14e5647c4..e94c271e7 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/nfp/nfp_user.h" -namespace Service { -namespace NFP { +namespace Service::NFP { NFP_User::NFP_User(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nfp:user") { @@ -15,5 +14,4 @@ NFP_User::NFP_User(std::shared_ptr<Module> module) RegisterHandlers(functions); } -} // namespace NFP -} // namespace Service +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 1606444ca..700043114 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -6,13 +6,11 @@ #include "core/hle/service/nfp/nfp.h" -namespace Service { -namespace NFP { +namespace Service::NFP { class NFP_User final : public Module::Interface { public: explicit NFP_User(std::shared_ptr<Module> module); }; -} // namespace NFP -} // namespace Service +} // namespace Service::NFP diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index b32112db3..df1e7f8fe 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -9,8 +9,7 @@ #include "core/hle/service/nifm/nifm_s.h" #include "core/hle/service/nifm/nifm_u.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { class IScanRequest final : public ServiceFramework<IScanRequest> { public: @@ -208,5 +207,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<NIFM_U>(module)->InstallAsService(service_manager); } -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 11d263b12..4ad3f3bcf 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { class Module final { public: @@ -25,5 +24,4 @@ public: void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm_a.cpp b/src/core/hle/service/nifm/nifm_a.cpp index f75df8c04..b7f296a20 100644 --- a/src/core/hle/service/nifm/nifm_a.cpp +++ b/src/core/hle/service/nifm/nifm_a.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/nifm/nifm_a.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") { static const FunctionInfo functions[] = { @@ -15,5 +14,4 @@ NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm_a.h b/src/core/hle/service/nifm/nifm_a.h index eaea14e29..c3ba33110 100644 --- a/src/core/hle/service/nifm/nifm_a.h +++ b/src/core/hle/service/nifm/nifm_a.h @@ -6,13 +6,11 @@ #include "core/hle/service/nifm/nifm.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { class NIFM_A final : public Module::Interface { public: explicit NIFM_A(std::shared_ptr<Module> module); }; -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm_s.cpp b/src/core/hle/service/nifm/nifm_s.cpp index 9c0b300e4..96e3c0cee 100644 --- a/src/core/hle/service/nifm/nifm_s.cpp +++ b/src/core/hle/service/nifm/nifm_s.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/nifm/nifm_s.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") { static const FunctionInfo functions[] = { @@ -15,5 +14,4 @@ NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm_s.h b/src/core/hle/service/nifm/nifm_s.h index f9e2d8039..8d1635a5d 100644 --- a/src/core/hle/service/nifm/nifm_s.h +++ b/src/core/hle/service/nifm/nifm_s.h @@ -6,13 +6,11 @@ #include "core/hle/service/nifm/nifm.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { class NIFM_S final : public Module::Interface { public: explicit NIFM_S(std::shared_ptr<Module> module); }; -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm_u.cpp b/src/core/hle/service/nifm/nifm_u.cpp index 44e6f483d..8cb75b903 100644 --- a/src/core/hle/service/nifm/nifm_u.cpp +++ b/src/core/hle/service/nifm/nifm_u.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/nifm/nifm_u.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") { static const FunctionInfo functions[] = { @@ -15,5 +14,4 @@ NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(mod RegisterHandlers(functions); } -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm_u.h b/src/core/hle/service/nifm/nifm_u.h index 912006775..def9726b1 100644 --- a/src/core/hle/service/nifm/nifm_u.h +++ b/src/core/hle/service/nifm/nifm_u.h @@ -6,13 +6,11 @@ #include "core/hle/service/nifm/nifm.h" -namespace Service { -namespace NIFM { +namespace Service::NIFM { class NIFM_U final : public Module::Interface { public: explicit NIFM_U(std::shared_ptr<Module> module); }; -} // namespace NIFM -} // namespace Service +} // namespace Service::NIFM diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 45681c50f..89c703310 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -5,12 +5,10 @@ #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pl_u.h" -namespace Service { -namespace NS { +namespace Service::NS { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<PL_U>()->InstallAsService(service_manager); } -} // namespace NS -} // namespace Service +} // namespace Service::NS diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index a4b7e3ded..b81ca8f1e 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -6,11 +6,9 @@ #include "core/hle/service/service.h" -namespace Service { -namespace NS { +namespace Service::NS { /// Registers all NS services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace NS -} // namespace Service +} // namespace Service::NS diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index d5e0b5f14..c416ad720 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -8,8 +8,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/ns/pl_u.h" -namespace Service { -namespace NS { +namespace Service::NS { struct FontRegion { u32 offset; @@ -47,10 +46,10 @@ PL_U::PL_U() : ServiceFramework("pl:u") { FileUtil::CreateFullPath(filepath); // Create path if not already created FileUtil::IOFile file(filepath, "rb"); + shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); if (file.IsOpen()) { // Read shared font data ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); - shared_font = std::make_shared<std::vector<u8>>(static_cast<size_t>(file.GetSize())); file.ReadBytes(shared_font->data(), shared_font->size()); } else { LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str()); @@ -97,22 +96,19 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { } void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { - if (shared_font != nullptr) { - // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared - // font data. This (likely) relies on exact address, size, and offsets from the original - // dump. In the future, we need to replace this with a more robust solution. - - // Map backing memory for the font data - Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0, - SHARED_FONT_MEM_SIZE, - Kernel::MemoryState::Shared); - - // Create shared font memory object - shared_font_mem = Kernel::SharedMemory::Create( - Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, - Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, - "PL_U:shared_font_mem"); - } + // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared + // font data. This (likely) relies on exact address, size, and offsets from the original + // dump. In the future, we need to replace this with a more robust solution. + + // Map backing memory for the font data + Core::CurrentProcess()->vm_manager.MapMemoryBlock( + SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared); + + // Create shared font memory object + shared_font_mem = Kernel::SharedMemory::Create( + Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, + "PL_U:shared_font_mem"); LOG_DEBUG(Service_NS, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; @@ -120,5 +116,4 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(shared_font_mem); } -} // namespace NS -} // namespace Service +} // namespace Service::NS diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h index 360482d13..b175c9c44 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/pl_u.h @@ -8,8 +8,7 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/service.h" -namespace Service { -namespace NS { +namespace Service::NS { class PL_U final : public ServiceFramework<PL_U> { public: @@ -30,5 +29,4 @@ private: std::shared_ptr<std::vector<u8>> shared_font; }; -} // namespace NS -} // namespace Service +} // namespace Service::NS diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index cdc25b059..0f02a1a18 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -9,9 +9,7 @@ #include "common/common_types.h" #include "common/swap.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to /// implement the ioctl interface. @@ -38,6 +36,4 @@ public: virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 87b3a2d74..61f22b1a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -10,9 +10,7 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { UNIMPLEMENTED(); @@ -35,6 +33,4 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 VideoCore::g_renderer->SwapBuffers(framebuffer); } -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 66f56f23d..3d3979723 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -10,9 +10,7 @@ #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvflinger/buffer_queue.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { class nvmap; @@ -31,6 +29,4 @@ private: std::shared_ptr<nvmap> nvmap_dev; }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9892402fa..71e844959 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -9,9 +9,7 @@ #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", @@ -115,6 +113,4 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o return 0; } -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index f8a60cce7..d86c3ebd9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -11,9 +11,7 @@ #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { class nvmap; @@ -100,6 +98,4 @@ private: std::shared_ptr<nvmap> nvmap_dev; }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 45711d686..660a0f665 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -6,9 +6,7 @@ #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", @@ -59,6 +57,4 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& return 0; } -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 0ca01aa6d..76a8b33c2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -11,9 +11,7 @@ #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: @@ -55,6 +53,4 @@ private: u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output); }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 3b353d742..18ea12ef5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -7,9 +7,7 @@ #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", @@ -122,6 +120,4 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& return 0; } -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index dc0476993..31040cdbe 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -9,9 +9,7 @@ #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { class nvhost_ctrl_gpu final : public nvdevice { public: @@ -125,6 +123,5 @@ private: u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service + +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index da44c65f3..a16e90457 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -9,9 +9,7 @@ #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", @@ -142,6 +140,4 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp return 0; } -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index e7e9a0088..703c36bbb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -10,9 +10,7 @@ #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { class nvmap; constexpr u32 NVGPU_IOCTL_MAGIC('H'); @@ -139,6 +137,4 @@ private: std::shared_ptr<nvmap> nvmap_dev; }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index b3842eb4c..4bb1f57f6 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -9,9 +9,7 @@ #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { VAddr nvmap::GetObjectAddress(u32 handle) const { auto object = GetObject(handle); @@ -144,6 +142,4 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { return 0; } -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 4681e438b..431eb3773 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -12,9 +12,7 @@ #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service { -namespace Nvidia { -namespace Devices { +namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: @@ -111,6 +109,4 @@ private: u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output); }; -} // namespace Devices -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 567c2cd7c..d0d64a840 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -9,8 +9,7 @@ #include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" -namespace Service { -namespace Nvidia { +namespace Service::Nvidia { void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); @@ -111,5 +110,4 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) query_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NVDRV::query_event"); } -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index daf2302af..959b5ba29 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -10,8 +10,7 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" -namespace Service { -namespace Nvidia { +namespace Service::Nvidia { class NVDRV final : public ServiceFramework<NVDRV> { public: @@ -34,5 +33,4 @@ private: Kernel::SharedPtr<Kernel::Event> query_event; }; -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index ea00240e6..170420418 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -14,8 +14,7 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvmemp.h" -namespace Service { -namespace Nvidia { +namespace Service::Nvidia { std::weak_ptr<Module> nvdrv; @@ -69,5 +68,4 @@ ResultCode Module::Close(u32 fd) { return RESULT_SUCCESS; } -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 6a55ff96d..579940817 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -10,8 +10,7 @@ #include "common/common_types.h" #include "core/hle/service/service.h" -namespace Service { -namespace Nvidia { +namespace Service::Nvidia { namespace Devices { class nvdevice; @@ -61,5 +60,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager); extern std::weak_ptr<Module> nvdrv; -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp index 35d6c0c13..9ca6e5512 100644 --- a/src/core/hle/service/nvdrv/nvmemp.cpp +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -8,8 +8,7 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvmemp.h" -namespace Service { -namespace Nvidia { +namespace Service::Nvidia { NVMEMP::NVMEMP() : ServiceFramework("nvmemp") { static const FunctionInfo functions[] = { @@ -27,5 +26,4 @@ void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) { UNIMPLEMENTED(); } -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index fb16026b0..dfdcabf4a 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Nvidia { +namespace Service::Nvidia { class NVMEMP final : public ServiceFramework<NVMEMP> { public: @@ -19,5 +18,4 @@ private: void Cmd1(Kernel::HLERequestContext& ctx); }; -} // namespace Nvidia -} // namespace Service +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index e4ff2e267..03a4fed59 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -9,8 +9,7 @@ #include "core/core_timing.h" #include "core/hle/service/nvflinger/buffer_queue.h" -namespace Service { -namespace NVFlinger { +namespace Service::NVFlinger { BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); @@ -111,5 +110,4 @@ void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_eve buffer_wait_event = std::move(wait_event); } -} // namespace NVFlinger -} // namespace Service +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 1de5767cb..95adc4706 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -13,8 +13,7 @@ namespace CoreTiming { struct EventType; } -namespace Service { -namespace NVFlinger { +namespace Service::NVFlinger { struct IGBPBuffer { u32_le magic; @@ -98,5 +97,4 @@ private: Kernel::SharedPtr<Kernel::Event> buffer_wait_event; }; -} // namespace NVFlinger -} // namespace Service +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 0d30f54dc..a99ebc8e6 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include "common/alignment.h" +#include "common/microprofile.h" #include "common/scope_exit.h" #include "core/core.h" #include "core/core_timing.h" @@ -15,8 +16,7 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" -namespace Service { -namespace NVFlinger { +namespace Service::NVFlinger { constexpr size_t SCREEN_REFRESH_RATE = 60; constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); @@ -128,6 +128,8 @@ void NVFlinger::Compose() { // Search for a queued buffer and acquire it auto buffer = buffer_queue->AcquireBuffer(); + MicroProfileFlip(); + if (buffer == boost::none) { // There was no queued buffer to draw, render previous frame Core::System::GetInstance().perf_stats.EndGameFrame(); @@ -162,5 +164,4 @@ Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event"); } -} // namespace NVFlinger -} // namespace Service +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 3126018ad..2c908297b 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -12,8 +12,7 @@ namespace CoreTiming { struct EventType; } -namespace Service { -namespace NVFlinger { +namespace Service::NVFlinger { class BufferQueue; @@ -80,5 +79,4 @@ private: CoreTiming::EventType* composition_event; }; -} // namespace NVFlinger -} // namespace Service +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index 692b27a71..6ee81866d 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp @@ -5,12 +5,10 @@ #include "core/hle/service/pctl/pctl.h" #include "core/hle/service/pctl/pctl_a.h" -namespace Service { -namespace PCTL { +namespace Service::PCTL { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<PCTL_A>()->InstallAsService(service_manager); } -} // namespace PCTL -} // namespace Service +} // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 5fa67dd1b..f0a84b115 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -6,11 +6,9 @@ #include "core/hle/service/service.h" -namespace Service { -namespace PCTL { +namespace Service::PCTL { /// Registers all PCTL services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace PCTL -} // namespace Service +} // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/pctl_a.cpp b/src/core/hle/service/pctl/pctl_a.cpp index 4e644be64..9fb4628ad 100644 --- a/src/core/hle/service/pctl/pctl_a.cpp +++ b/src/core/hle/service/pctl/pctl_a.cpp @@ -6,8 +6,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/pctl/pctl_a.h" -namespace Service { -namespace PCTL { +namespace Service::PCTL { class IParentalControlService final : public ServiceFramework<IParentalControlService> { public: @@ -125,5 +124,4 @@ PCTL_A::PCTL_A() : ServiceFramework("pctl:a") { RegisterHandlers(functions); } -} // namespace PCTL -} // namespace Service +} // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/pctl_a.h b/src/core/hle/service/pctl/pctl_a.h index 3aa8873a9..09ed82e1b 100644 --- a/src/core/hle/service/pctl/pctl_a.h +++ b/src/core/hle/service/pctl/pctl_a.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace PCTL { +namespace Service::PCTL { class PCTL_A final : public ServiceFramework<PCTL_A> { public: @@ -18,5 +17,4 @@ private: void CreateService(Kernel::HLERequestContext& ctx); }; -} // namespace PCTL -} // namespace Service +} // namespace Service::PCTL diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index c5490c1ae..08ce29677 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -145,7 +145,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); } case IPC::CommandType::Control: { - SM::g_service_manager->InvokeControlRequest(context); + Core::System::GetInstance().ServiceManager().InvokeControlRequest(context); break; } case IPC::CommandType::Request: { @@ -170,42 +170,40 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { } /// Initialize ServiceManager -void Init() { +void Init(std::shared_ptr<SM::ServiceManager>& sm) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); - SM::g_service_manager = std::make_shared<SM::ServiceManager>(); - SM::ServiceManager::InstallInterfaces(SM::g_service_manager); - - Account::InstallInterfaces(*SM::g_service_manager); - AM::InstallInterfaces(*SM::g_service_manager, nv_flinger); - AOC::InstallInterfaces(*SM::g_service_manager); - APM::InstallInterfaces(*SM::g_service_manager); - Audio::InstallInterfaces(*SM::g_service_manager); - Fatal::InstallInterfaces(*SM::g_service_manager); - FileSystem::InstallInterfaces(*SM::g_service_manager); - Friend::InstallInterfaces(*SM::g_service_manager); - HID::InstallInterfaces(*SM::g_service_manager); - LM::InstallInterfaces(*SM::g_service_manager); - NFP::InstallInterfaces(*SM::g_service_manager); - NIFM::InstallInterfaces(*SM::g_service_manager); - NS::InstallInterfaces(*SM::g_service_manager); - Nvidia::InstallInterfaces(*SM::g_service_manager); - PCTL::InstallInterfaces(*SM::g_service_manager); - Sockets::InstallInterfaces(*SM::g_service_manager); - SPL::InstallInterfaces(*SM::g_service_manager); - SSL::InstallInterfaces(*SM::g_service_manager); - Time::InstallInterfaces(*SM::g_service_manager); - VI::InstallInterfaces(*SM::g_service_manager, nv_flinger); - Set::InstallInterfaces(*SM::g_service_manager); + SM::ServiceManager::InstallInterfaces(sm); + + Account::InstallInterfaces(*sm); + AM::InstallInterfaces(*sm, nv_flinger); + AOC::InstallInterfaces(*sm); + APM::InstallInterfaces(*sm); + Audio::InstallInterfaces(*sm); + Fatal::InstallInterfaces(*sm); + FileSystem::InstallInterfaces(*sm); + Friend::InstallInterfaces(*sm); + HID::InstallInterfaces(*sm); + LM::InstallInterfaces(*sm); + NFP::InstallInterfaces(*sm); + NIFM::InstallInterfaces(*sm); + NS::InstallInterfaces(*sm); + Nvidia::InstallInterfaces(*sm); + PCTL::InstallInterfaces(*sm); + Sockets::InstallInterfaces(*sm); + SPL::InstallInterfaces(*sm); + SSL::InstallInterfaces(*sm); + Time::InstallInterfaces(*sm); + VI::InstallInterfaces(*sm, nv_flinger); + Set::InstallInterfaces(*sm); LOG_DEBUG(Service, "initialized OK"); } /// Shutdown ServiceManager void Shutdown() { - SM::g_service_manager = nullptr; g_kernel_named_ports.clear(); LOG_DEBUG(Service, "shutdown OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 9c2e826da..fee841d46 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -178,7 +178,7 @@ private: }; /// Initialize ServiceManager -void Init(); +void Init(std::shared_ptr<SM::ServiceManager>& sm); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index aa7c924e7..fc3e424d0 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -9,8 +9,7 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/service/set/set.h" -namespace Service { -namespace Set { +namespace Service::Set { void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -36,9 +35,9 @@ SET::SET() : ServiceFramework("set") { {5, nullptr, "GetAvailableLanguageCodes2"}, {6, nullptr, "GetAvailableLanguageCodeCount2"}, {7, nullptr, "GetKeyCodeMap"}, + {8, nullptr, "GetQuestFlag"}, }; RegisterHandlers(functions); } -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 7b7814ed1..6a465949f 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Set { +namespace Service::Set { class SET final : public ServiceFramework<SET> { public: @@ -18,5 +17,4 @@ private: void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); }; -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 6231acd96..7066ef725 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/set/set_cal.h" -namespace Service { -namespace Set { +namespace Service::Set { SET_CAL::SET_CAL() : ServiceFramework("set:cal") { static const FunctionInfo functions[] = { @@ -32,9 +31,17 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { {21, nullptr, "GetEticketDeviceKey"}, {22, nullptr, "GetSpeakerParameter"}, {23, nullptr, "GetLcdVendorId"}, + {24, nullptr, "GetEciDeviceCertificate2"}, + {25, nullptr, "GetEciDeviceKey2"}, + {26, nullptr, "GetAmiiboKey"}, + {27, nullptr, "GetAmiiboEcqvCertificate"}, + {28, nullptr, "GetAmiiboEcdsaCertificate"}, + {29, nullptr, "GetAmiiboEcqvBlsKey"}, + {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, + {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, + {32, nullptr, "GetUnknownId"}, }; RegisterHandlers(functions); } -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h index 9c0b851d0..bb50336aa 100644 --- a/src/core/hle/service/set/set_cal.h +++ b/src/core/hle/service/set/set_cal.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Set { +namespace Service::Set { class SET_CAL final : public ServiceFramework<SET_CAL> { public: @@ -15,5 +14,4 @@ public: ~SET_CAL() = default; }; -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp index 8320d4250..c9f938716 100644 --- a/src/core/hle/service/set/set_fd.cpp +++ b/src/core/hle/service/set/set_fd.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/set/set_fd.h" -namespace Service { -namespace Set { +namespace Service::Set { SET_FD::SET_FD() : ServiceFramework("set:fd") { static const FunctionInfo functions[] = { @@ -21,5 +20,4 @@ SET_FD::SET_FD() : ServiceFramework("set:fd") { RegisterHandlers(functions); } -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h index 65b36bcb3..dbd850bc7 100644 --- a/src/core/hle/service/set/set_fd.h +++ b/src/core/hle/service/set/set_fd.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Set { +namespace Service::Set { class SET_FD final : public ServiceFramework<SET_FD> { public: @@ -15,5 +14,4 @@ public: ~SET_FD() = default; }; -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 363abd10a..fa85277fe 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -7,8 +7,7 @@ #include "core/hle/kernel/client_port.h" #include "core/hle/service/set/set_sys.h" -namespace Service { -namespace Set { +namespace Service::Set { void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { @@ -27,6 +26,7 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") { {2, nullptr, "GetNetworkSettings"}, {3, nullptr, "GetFirmwareVersion"}, {4, nullptr, "GetFirmwareVersion2"}, + {5, nullptr, "GetFirmwareVersionDigest"}, {7, nullptr, "GetLockScreenFlag"}, {8, nullptr, "SetLockScreenFlag"}, {9, nullptr, "GetBacklightSettings"}, @@ -159,9 +159,17 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") { {138, nullptr, "GetWebInspectorFlag"}, {139, nullptr, "GetAllowedSslHosts"}, {140, nullptr, "GetHostFsMountPoint"}, + {141, nullptr, "GetRequiresRunRepairTimeReviser"}, + {142, nullptr, "SetRequiresRunRepairTimeReviser"}, + {143, nullptr, "SetBlePairingSettings"}, + {144, nullptr, "GetBlePairingSettings"}, + {145, nullptr, "GetConsoleSixAxisSensorAngularVelocityTimeBias"}, + {146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"}, + {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, + {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, + {149, nullptr, "GetRebootlessSystemUpdateVersion"}, }; RegisterHandlers(functions); } -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 105f1a3c7..b77a97cde 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Set { +namespace Service::Set { class SET_SYS final : public ServiceFramework<SET_SYS> { public: @@ -18,5 +17,4 @@ private: void GetColorSetId(Kernel::HLERequestContext& ctx); }; -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp index c6bc9e240..cf5541ca8 100644 --- a/src/core/hle/service/set/settings.cpp +++ b/src/core/hle/service/set/settings.cpp @@ -8,8 +8,7 @@ #include "core/hle/service/set/set_sys.h" #include "core/hle/service/set/settings.h" -namespace Service { -namespace Set { +namespace Service::Set { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<SET>()->InstallAsService(service_manager); @@ -18,5 +17,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<SET_SYS>()->InstallAsService(service_manager); } -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h index 6c8d5a58c..6606ce776 100644 --- a/src/core/hle/service/set/settings.h +++ b/src/core/hle/service/set/settings.h @@ -6,11 +6,9 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Set { +namespace Service::Set { /// Registers all Settings services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Set -} // namespace Service +} // namespace Service::Set diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index e12c53442..13e31620d 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -7,8 +7,7 @@ #include "core/hle/kernel/session.h" #include "core/hle/service/sm/controller.h" -namespace Service { -namespace SM { +namespace Service::SM { void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { ASSERT_MSG(!ctx.Session()->IsDomain(), "session is alread a domain"); @@ -58,5 +57,4 @@ Controller::Controller() : ServiceFramework("IpcController") { RegisterHandlers(functions); } -} // namespace SM -} // namespace Service +} // namespace Service::SM diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/controller.h index 7b4bc4b75..a4de52cd2 100644 --- a/src/core/hle/service/sm/controller.h +++ b/src/core/hle/service/sm/controller.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace SM { +namespace Service::SM { class Controller final : public ServiceFramework<Controller> { public: @@ -21,5 +20,4 @@ private: void QueryPointerBufferSize(Kernel::HLERequestContext& ctx); }; -} // namespace SM -} // namespace Service +} // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index bc72512a0..4578fc05f 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -12,8 +12,9 @@ #include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" -namespace Service { -namespace SM { +namespace Service::SM { + +ServiceManager::~ServiceManager() = default; void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); @@ -73,7 +74,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToSer return client_port->Connect(); } -std::shared_ptr<ServiceManager> g_service_manager; +SM::~SM() = default; /** * SM::Initialize service function @@ -132,5 +133,4 @@ SM::SM(std::shared_ptr<ServiceManager> service_manager) RegisterHandlers(functions); } -} // namespace SM -} // namespace Service +} // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 11fa788ca..13f5c4c28 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -17,14 +17,13 @@ class ServerPort; class SessionRequestHandler; } // namespace Kernel -namespace Service { -namespace SM { +namespace Service::SM { /// Interface to "sm:" service class SM final : public ServiceFramework<SM> { public: SM(std::shared_ptr<ServiceManager> service_manager); - ~SM() = default; + ~SM() override; private: void Initialize(Kernel::HLERequestContext& ctx); @@ -45,6 +44,8 @@ class ServiceManager { public: static void InstallInterfaces(std::shared_ptr<ServiceManager> self); + ~ServiceManager(); + ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, unsigned int max_sessions); ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); @@ -60,7 +61,4 @@ private: std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services; }; -extern std::shared_ptr<ServiceManager> g_service_manager; - -} // namespace SM -} // namespace Service +} // namespace Service::SM diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 790ff82b3..f99809bed 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -5,8 +5,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/sockets/bsd.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service, "(STUBBED) called"); @@ -79,12 +78,36 @@ BSD::BSD(const char* name) : ServiceFramework(name) { {0, &BSD::RegisterClient, "RegisterClient"}, {1, &BSD::StartMonitoring, "StartMonitoring"}, {2, &BSD::Socket, "Socket"}, + {3, nullptr, "SocketExempt"}, + {4, nullptr, "Open"}, + {5, nullptr, "Select"}, + {6, nullptr, "Poll"}, + {7, nullptr, "Sysctl"}, + {8, nullptr, "Recv"}, + {9, nullptr, "RecvFrom"}, + {10, nullptr, "Send"}, {11, &BSD::SendTo, "SendTo"}, + {12, nullptr, "Accept"}, + {13, nullptr, "Bind"}, {14, &BSD::Connect, "Connect"}, + {15, nullptr, "GetPeerName"}, + {16, nullptr, "GetSockName"}, + {17, nullptr, "GetSockOpt"}, + {18, nullptr, "Listen"}, + {19, nullptr, "Ioctl"}, + {20, nullptr, "Fcntl"}, + {21, nullptr, "SetSockOpt"}, + {22, nullptr, "Shutdown"}, + {23, nullptr, "ShutdownAllSockets"}, + {24, nullptr, "Write"}, + {25, nullptr, "Read"}, {26, &BSD::Close, "Close"}, + {27, nullptr, "DuplicateSocket"}, + {28, nullptr, "GetResourceStatistics"}, + {29, nullptr, "RecvMMsg"}, + {30, nullptr, "SendMMsg"}, }; RegisterHandlers(functions); } -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 32d949e95..a6b1ca7d0 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -7,8 +7,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { class BSD final : public ServiceFramework<BSD> { public: @@ -27,5 +26,4 @@ private: u32 next_fd = 1; }; -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index e3542d325..8682dc2e0 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/sockets/nsd.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { NSD::NSD(const char* name) : ServiceFramework(name) { static const FunctionInfo functions[] = { @@ -30,5 +29,4 @@ NSD::NSD(const char* name) : ServiceFramework(name) { RegisterHandlers(functions); } -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h index a7c15a860..3b7edfc43 100644 --- a/src/core/hle/service/sockets/nsd.h +++ b/src/core/hle/service/sockets/nsd.h @@ -7,8 +7,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { class NSD final : public ServiceFramework<NSD> { public: @@ -16,5 +15,4 @@ public: ~NSD() = default; }; -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index eb4b5fa57..d235c4cfd 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -5,8 +5,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/sockets/sfdnsres.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -30,9 +29,9 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") { {7, nullptr, "GetNameInfo"}, {8, nullptr, "RequestCancelHandle"}, {9, nullptr, "CancelSocketCall"}, + {11, nullptr, "ClearDnsIpServerAddressArray"}, }; RegisterHandlers(functions); } -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index c07cc1594..62c7e35bf 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -7,8 +7,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { class SFDNSRES final : public ServiceFramework<SFDNSRES> { public: @@ -19,5 +18,4 @@ private: void GetAddrInfo(Kernel::HLERequestContext& ctx); }; -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp index cedc276d9..05bd10d35 100644 --- a/src/core/hle/service/sockets/sockets.cpp +++ b/src/core/hle/service/sockets/sockets.cpp @@ -7,8 +7,7 @@ #include "core/hle/service/sockets/sfdnsres.h" #include "core/hle/service/sockets/sockets.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager); @@ -18,5 +17,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<SFDNSRES>()->InstallAsService(service_manager); } -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 7e89c8d2c..ca8a6a7e0 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -6,11 +6,9 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Sockets { +namespace Service::Sockets { /// Registers all Sockets services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Sockets -} // namespace Service +} // namespace Service::Sockets diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp index cde05717a..b9e6b799d 100644 --- a/src/core/hle/service/spl/csrng.cpp +++ b/src/core/hle/service/spl/csrng.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/spl/csrng.h" -namespace Service { -namespace SPL { +namespace Service::SPL { CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "csrng") { static const FunctionInfo functions[] = { @@ -14,5 +13,4 @@ CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(modul RegisterHandlers(functions); } -} // namespace SPL -} // namespace Service +} // namespace Service::SPL diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h index 59ca794dd..3f849b5a7 100644 --- a/src/core/hle/service/spl/csrng.h +++ b/src/core/hle/service/spl/csrng.h @@ -6,13 +6,11 @@ #include "core/hle/service/spl/module.h" -namespace Service { -namespace SPL { +namespace Service::SPL { class CSRNG final : public Module::Interface { public: explicit CSRNG(std::shared_ptr<Module> module); }; -} // namespace SPL -} // namespace Service +} // namespace Service::SPL diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index fc1bcd94c..3f5a342a7 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -11,8 +11,7 @@ #include "core/hle/service/spl/module.h" #include "core/hle/service/spl/spl.h" -namespace Service { -namespace SPL { +namespace Service::SPL { Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) : ServiceFramework(name), module(std::move(module)) {} @@ -38,5 +37,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<SPL>(module)->InstallAsService(service_manager); } -} // namespace SPL -} // namespace Service +} // namespace Service::SPL diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h index 12cdb2980..6ab91b400 100644 --- a/src/core/hle/service/spl/module.h +++ b/src/core/hle/service/spl/module.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace SPL { +namespace Service::SPL { class Module final { public: @@ -25,5 +24,4 @@ public: /// Registers all SPL services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace SPL -} // namespace Service +} // namespace Service::SPL diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp index deab29b91..bb1e03342 100644 --- a/src/core/hle/service/spl/spl.cpp +++ b/src/core/hle/service/spl/spl.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/spl/spl.h" -namespace Service { -namespace SPL { +namespace Service::SPL { SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") { static const FunctionInfo functions[] = { @@ -33,9 +32,14 @@ SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), {23, nullptr, "GetSplWaitEvent"}, {24, nullptr, "SetSharedData"}, {25, nullptr, "GetSharedData"}, + {26, nullptr, "ImportSslRsaKey"}, + {27, nullptr, "SecureExpModWithSslKey"}, + {28, nullptr, "ImportEsRsaKey"}, + {29, nullptr, "SecureExpModWithEsKey"}, + {30, nullptr, "EncryptManuRsaKeyForImport"}, + {31, nullptr, "GetPackage2Hash"}, }; RegisterHandlers(functions); } -} // namespace SPL -} // namespace Service +} // namespace Service::SPL diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h index 9fd6059af..69c4c1747 100644 --- a/src/core/hle/service/spl/spl.h +++ b/src/core/hle/service/spl/spl.h @@ -6,13 +6,11 @@ #include "core/hle/service/spl/module.h" -namespace Service { -namespace SPL { +namespace Service::SPL { class SPL final : public Module::Interface { public: explicit SPL(std::shared_ptr<Module> module); }; -} // namespace SPL -} // namespace Service +} // namespace Service::SPL diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index 01a03ec83..11d438728 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -5,8 +5,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/ssl/ssl.h" -namespace Service { -namespace SSL { +namespace Service::SSL { class ISslConnection final : public ServiceFramework<ISslConnection> { public: @@ -107,5 +106,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<SSL>()->InstallAsService(service_manager); } -} // namespace SSL -} // namespace Service +} // namespace Service::SSL diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h index 7fcff5ccd..87538a639 100644 --- a/src/core/hle/service/ssl/ssl.h +++ b/src/core/hle/service/ssl/ssl.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace SSL { +namespace Service::SSL { class SSL final : public ServiceFramework<SSL> { public: @@ -21,5 +20,4 @@ private: /// Registers all SSL services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace SSL -} // namespace Service +} // namespace Service::SSL diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index c3e46f866..2604ecc1c 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -12,15 +12,18 @@ #include "core/hle/service/time/time_s.h" #include "core/hle/service/time/time_u.h" -namespace Service { -namespace Time { +namespace Service::Time { class ISystemClock final : public ServiceFramework<ISystemClock> { public: ISystemClock() : ServiceFramework("ISystemClock") { static const FunctionInfo functions[] = { {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, - {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}}; + {1, nullptr, "SetCurrentTime"}, + {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}, + {3, nullptr, "SetSystemClockContext"}, + + }; RegisterHandlers(functions); } @@ -162,5 +165,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<TIME_U>(time)->InstallAsService(service_manager); } -} // namespace Time -} // namespace Service +} // namespace Service::Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 197029e7a..12fe1995a 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -6,8 +6,7 @@ #include "core/hle/service/service.h" -namespace Service { -namespace Time { +namespace Service::Time { // TODO(Rozelette) RE this structure struct LocationName { @@ -66,5 +65,4 @@ public: /// Registers all Time services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); -} // namespace Time -} // namespace Service +} // namespace Service::Time diff --git a/src/core/hle/service/time/time_s.cpp b/src/core/hle/service/time/time_s.cpp index b172b2bd6..0b599ea00 100644 --- a/src/core/hle/service/time/time_s.cpp +++ b/src/core/hle/service/time/time_s.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/time/time_s.h" -namespace Service { -namespace Time { +namespace Service::Time { TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:s") { static const FunctionInfo functions[] = { @@ -14,9 +13,19 @@ TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time) {2, &TIME_S::GetStandardSteadyClock, "GetStandardSteadyClock"}, {3, &TIME_S::GetTimeZoneService, "GetTimeZoneService"}, {4, &TIME_S::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, nullptr, "GetEphemeralNetworkSystemClock"}, + {50, nullptr, "SetStandardSteadyClockInternalOffset"}, + {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, nullptr, "GetStandardUserSystemClockInitialYear"}, + {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, + {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, nullptr, "GetClockSnapshot"}, + {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, + {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, nullptr, "CalculateSpanBetween"}, }; RegisterHandlers(functions); } -} // namespace Time -} // namespace Service +} // namespace Service::Time diff --git a/src/core/hle/service/time/time_s.h b/src/core/hle/service/time/time_s.h index abc2a8c5a..4a2daa513 100644 --- a/src/core/hle/service/time/time_s.h +++ b/src/core/hle/service/time/time_s.h @@ -6,13 +6,11 @@ #include "core/hle/service/time/time.h" -namespace Service { -namespace Time { +namespace Service::Time { class TIME_S final : public Module::Interface { public: explicit TIME_S(std::shared_ptr<Module> time); }; -} // namespace Time -} // namespace Service +} // namespace Service::Time diff --git a/src/core/hle/service/time/time_u.cpp b/src/core/hle/service/time/time_u.cpp index fc1ace325..1ed42c419 100644 --- a/src/core/hle/service/time/time_u.cpp +++ b/src/core/hle/service/time/time_u.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/time/time_u.h" -namespace Service { -namespace Time { +namespace Service::Time { TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:u") { static const FunctionInfo functions[] = { @@ -14,9 +13,19 @@ TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time) {2, &TIME_U::GetStandardSteadyClock, "GetStandardSteadyClock"}, {3, &TIME_U::GetTimeZoneService, "GetTimeZoneService"}, {4, &TIME_U::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, nullptr, "GetEphemeralNetworkSystemClock"}, + {50, nullptr, "SetStandardSteadyClockInternalOffset"}, + {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, nullptr, "GetStandardUserSystemClockInitialYear"}, + {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, + {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, nullptr, "GetClockSnapshot"}, + {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, + {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, nullptr, "CalculateSpanBetween"}, }; RegisterHandlers(functions); } -} // namespace Time -} // namespace Service +} // namespace Service::Time diff --git a/src/core/hle/service/time/time_u.h b/src/core/hle/service/time/time_u.h index f99d25057..3724bcdc7 100644 --- a/src/core/hle/service/time/time_u.h +++ b/src/core/hle/service/time/time_u.h @@ -6,13 +6,11 @@ #include "core/hle/service/time/time.h" -namespace Service { -namespace Time { +namespace Service::Time { class TIME_U final : public Module::Interface { public: explicit TIME_U(std::shared_ptr<Module> time); }; -} // namespace Time -} // namespace Service +} // namespace Service::Time diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 715206493..36ae2215f 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -21,8 +21,7 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" -namespace Service { -namespace VI { +namespace Service::VI { struct DisplayInfo { char display_name[0x40]{"Default"}; @@ -150,7 +149,7 @@ private: class NativeWindow : public Parcel { public: - explicit NativeWindow(u32 id) : Parcel() { + explicit NativeWindow(u32 id) { data.id = id; } ~NativeWindow() override = default; @@ -197,7 +196,7 @@ public: class IGBPConnectResponseParcel : public Parcel { public: - explicit IGBPConnectResponseParcel(u32 width, u32 height) : Parcel() { + explicit IGBPConnectResponseParcel(u32 width, u32 height) { data.width = width; data.height = height; } @@ -247,10 +246,6 @@ public: }; class IGBPSetPreallocatedBufferResponseParcel : public Parcel { -public: - IGBPSetPreallocatedBufferResponseParcel() : Parcel() {} - ~IGBPSetPreallocatedBufferResponseParcel() override = default; - protected: void SerializeData() override { // TODO(Subv): Find out what this means @@ -289,7 +284,7 @@ static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong class IGBPDequeueBufferResponseParcel : public Parcel { public: - explicit IGBPDequeueBufferResponseParcel(u32 slot) : Parcel(), slot(slot) {} + explicit IGBPDequeueBufferResponseParcel(u32 slot) : slot(slot) {} ~IGBPDequeueBufferResponseParcel() override = default; protected: @@ -383,7 +378,7 @@ public: class IGBPQueueBufferResponseParcel : public Parcel { public: - explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) : Parcel() { + explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) { data.width = width; data.height = height; } @@ -424,7 +419,7 @@ public: class IGBPQueryResponseParcel : public Parcel { public: - explicit IGBPQueryResponseParcel(u32 value) : Parcel(), value(value) {} + explicit IGBPQueryResponseParcel(u32 value) : value(value) {} ~IGBPQueryResponseParcel() override = default; protected: @@ -580,7 +575,48 @@ public: ISystemDisplayService() : ServiceFramework("ISystemDisplayService") { static const FunctionInfo functions[] = { {1200, nullptr, "GetZOrderCountMin"}, + {1202, nullptr, "GetZOrderCountMax"}, + {1203, nullptr, "GetDisplayLogicalResolution"}, + {1204, nullptr, "SetDisplayMagnification"}, + {2201, nullptr, "SetLayerPosition"}, + {2203, nullptr, "SetLayerSize"}, + {2204, nullptr, "GetLayerZ"}, {2205, &ISystemDisplayService::SetLayerZ, "SetLayerZ"}, + {2207, &ISystemDisplayService::SetLayerVisibility, "SetLayerVisibility"}, + {2209, nullptr, "SetLayerAlpha"}, + {2312, nullptr, "CreateStrayLayer"}, + {2400, nullptr, "OpenIndirectLayer"}, + {2401, nullptr, "CloseIndirectLayer"}, + {2402, nullptr, "FlipIndirectLayer"}, + {3000, nullptr, "ListDisplayModes"}, + {3001, nullptr, "ListDisplayRgbRanges"}, + {3002, nullptr, "ListDisplayContentTypes"}, + {3200, nullptr, "GetDisplayMode"}, + {3201, nullptr, "SetDisplayMode"}, + {3202, nullptr, "GetDisplayUnderscan"}, + {3203, nullptr, "SetDisplayUnderscan"}, + {3204, nullptr, "GetDisplayContentType"}, + {3205, nullptr, "SetDisplayContentType"}, + {3206, nullptr, "GetDisplayRgbRange"}, + {3207, nullptr, "SetDisplayRgbRange"}, + {3208, nullptr, "GetDisplayCmuMode"}, + {3209, nullptr, "SetDisplayCmuMode"}, + {3210, nullptr, "GetDisplayContrastRatio"}, + {3211, nullptr, "SetDisplayContrastRatio"}, + {3214, nullptr, "GetDisplayGamma"}, + {3215, nullptr, "SetDisplayGamma"}, + {3216, nullptr, "GetDisplayCmuLuma"}, + {3217, nullptr, "SetDisplayCmuLuma"}, + {8225, nullptr, "GetSharedBufferMemoryHandleId"}, + {8250, nullptr, "OpenSharedLayer"}, + {8251, nullptr, "CloseSharedLayer"}, + {8252, nullptr, "ConnectSharedLayer"}, + {8253, nullptr, "DisconnectSharedLayer"}, + {8254, nullptr, "AcquireSharedFrameBuffer"}, + {8255, nullptr, "PresentSharedFrameBuffer"}, + {8256, nullptr, "GetSharedFrameBufferAcquirableEvent"}, + {8257, nullptr, "FillSharedFrameBufferColor"}, + {8258, nullptr, "CancelSharedFrameBuffer"}, }; RegisterHandlers(functions); } @@ -596,6 +632,16 @@ private: IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } + + void SetLayerVisibility(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 layer_id = rp.Pop<u64>(); + bool visibility = rp.Pop<bool>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x%x, visibility=%u", layer_id, + visibility); + } }; class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { @@ -603,10 +649,72 @@ public: explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { + {200, nullptr, "AllocateProcessHeapBlock"}, + {201, nullptr, "FreeProcessHeapBlock"}, {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"}, {1102, nullptr, "GetDisplayResolution"}, {2010, &IManagerDisplayService::CreateManagedLayer, "CreateManagedLayer"}, + {2011, nullptr, "DestroyManagedLayer"}, + {2050, nullptr, "CreateIndirectLayer"}, + {2051, nullptr, "DestroyIndirectLayer"}, + {2052, nullptr, "CreateIndirectProducerEndPoint"}, + {2053, nullptr, "DestroyIndirectProducerEndPoint"}, + {2054, nullptr, "CreateIndirectConsumerEndPoint"}, + {2055, nullptr, "DestroyIndirectConsumerEndPoint"}, + {2300, nullptr, "AcquireLayerTexturePresentingEvent"}, + {2301, nullptr, "ReleaseLayerTexturePresentingEvent"}, + {2302, nullptr, "GetDisplayHotplugEvent"}, + {2402, nullptr, "GetDisplayHotplugState"}, + {2501, nullptr, "GetCompositorErrorInfo"}, + {2601, nullptr, "GetDisplayErrorEvent"}, + {4201, nullptr, "SetDisplayAlpha"}, + {4203, nullptr, "SetDisplayLayerStack"}, + {4205, nullptr, "SetDisplayPowerState"}, + {4206, nullptr, "SetDefaultDisplay"}, {6000, &IManagerDisplayService::AddToLayerStack, "AddToLayerStack"}, + {6001, nullptr, "RemoveFromLayerStack"}, + {6002, &IManagerDisplayService::SetLayerVisibility, "SetLayerVisibility"}, + {6003, nullptr, "SetLayerConfig"}, + {6004, nullptr, "AttachLayerPresentationTracer"}, + {6005, nullptr, "DetachLayerPresentationTracer"}, + {6006, nullptr, "StartLayerPresentationRecording"}, + {6007, nullptr, "StopLayerPresentationRecording"}, + {6008, nullptr, "StartLayerPresentationFenceWait"}, + {6009, nullptr, "StopLayerPresentationFenceWait"}, + {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"}, + {7000, nullptr, "SetContentVisibility"}, + {8000, nullptr, "SetConductorLayer"}, + {8100, nullptr, "SetIndirectProducerFlipOffset"}, + {8200, nullptr, "CreateSharedBufferStaticStorage"}, + {8201, nullptr, "CreateSharedBufferTransferMemory"}, + {8202, nullptr, "DestroySharedBuffer"}, + {8203, nullptr, "BindSharedLowLevelLayerToManagedLayer"}, + {8204, nullptr, "BindSharedLowLevelLayerToIndirectLayer"}, + {8207, nullptr, "UnbindSharedLowLevelLayer"}, + {8208, nullptr, "ConnectSharedLowLevelLayerToSharedBuffer"}, + {8209, nullptr, "DisconnectSharedLowLevelLayerFromSharedBuffer"}, + {8210, nullptr, "CreateSharedLayer"}, + {8211, nullptr, "DestroySharedLayer"}, + {8216, nullptr, "AttachSharedLayerToLowLevelLayer"}, + {8217, nullptr, "ForceDetachSharedLayerFromLowLevelLayer"}, + {8218, nullptr, "StartDetachSharedLayerFromLowLevelLayer"}, + {8219, nullptr, "FinishDetachSharedLayerFromLowLevelLayer"}, + {8220, nullptr, "GetSharedLayerDetachReadyEvent"}, + {8221, nullptr, "GetSharedLowLevelLayerSynchronizedEvent"}, + {8222, nullptr, "CheckSharedLowLevelLayerSynchronized"}, + {8223, nullptr, "RegisterSharedBufferImporterAruid"}, + {8224, nullptr, "UnregisterSharedBufferImporterAruid"}, + {8227, nullptr, "CreateSharedBufferProcessHeap"}, + {8228, nullptr, "GetSharedLayerLayerStacks"}, + {8229, nullptr, "SetSharedLayerLayerStacks"}, + {8291, nullptr, "PresentDetachedSharedFrameBufferToLowLevelLayer"}, + {8292, nullptr, "FillDetachedSharedFrameBufferColor"}, + {8293, nullptr, "GetDetachedSharedFrameBufferImage"}, + {8294, nullptr, "SetDetachedSharedFrameBufferImage"}, + {8295, nullptr, "CopyDetachedSharedFrameBufferImage"}, + {8296, nullptr, "SetDetachedSharedFrameBufferSubImage"}, + {8297, nullptr, "GetSharedFrameBufferContentParameter"}, + {8298, nullptr, "ExpandStartupLogoOnSharedFrameBuffer"}, }; RegisterHandlers(functions); } @@ -647,6 +755,16 @@ private: rb.Push(RESULT_SUCCESS); } + void SetLayerVisibility(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 layer_id = rp.Pop<u64>(); + bool visibility = rp.Pop<bool>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x%x, visibility=%u", layer_id, + visibility); + } + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; @@ -717,15 +835,15 @@ private: IPC::RequestParser rp{ctx}; u64 display_id = rp.Pop<u64>(); - IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); rb.Push(RESULT_SUCCESS); if (Settings::values.use_docked_mode) { - rb.Push(static_cast<u32>(DisplayResolution::DockedWidth)); - rb.Push(static_cast<u32>(DisplayResolution::DockedHeight)); + rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); + rb.Push(static_cast<u64>(DisplayResolution::DockedHeight)); } else { - rb.Push(static_cast<u32>(DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u32>(DisplayResolution::UndockedHeight)); + rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); + rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); } } @@ -825,13 +943,21 @@ IApplicationDisplayService::IApplicationDisplayService( "GetIndirectDisplayTransactionService"}, {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, + {1011, nullptr, "OpenDefaultDisplay"}, {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, + {1101, nullptr, "SetDisplayEnabled"}, {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, - {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, + {2021, nullptr, "CloseLayer"}, {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, + {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, + {2102, nullptr, "ConvertScalingMode"}, + {2450, nullptr, "GetIndirectLayerImageMap"}, + {2451, nullptr, "GetIndirectLayerImageCropMap"}, + {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, + {5203, nullptr, "GetDisplayVsyncEventForDebug"}, }; RegisterHandlers(functions); } @@ -856,5 +982,4 @@ void InstallInterfaces(SM::ServiceManager& service_manager, std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager); } -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 7f16fad8e..e8bda01d7 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -11,8 +11,7 @@ namespace CoreTiming { struct EventType; } -namespace Service { -namespace VI { +namespace Service::VI { enum class DisplayResolution : u32 { DockedWidth = 1920, @@ -40,5 +39,4 @@ public: void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 5781fa9ec..d47da565b 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/vi/vi_m.h" -namespace Service { -namespace VI { +namespace Service::VI { VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) { @@ -16,5 +15,4 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> RegisterHandlers(functions); } -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 0f7b799d6..6abb9b3a3 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -6,13 +6,11 @@ #include "core/hle/service/vi/vi.h" -namespace Service { -namespace VI { +namespace Service::VI { class VI_M final : public Module::Interface { public: explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); }; -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 1f937b2a8..8f82e797f 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -4,8 +4,7 @@ #include "core/hle/service/vi/vi_s.h" -namespace Service { -namespace VI { +namespace Service::VI { VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) { @@ -16,5 +15,4 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> RegisterHandlers(functions); } -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 7b32fdddc..8f16f804f 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -6,13 +6,11 @@ #include "core/hle/service/vi/vi.h" -namespace Service { -namespace VI { +namespace Service::VI { class VI_S final : public Module::Interface { public: explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); }; -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index 14e375b86..b84aed1d5 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -4,17 +4,14 @@ #include "core/hle/service/vi/vi_u.h" -namespace Service { -namespace VI { +namespace Service::VI { VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) { static const FunctionInfo functions[] = { {0, &VI_U::GetDisplayService, "GetDisplayService"}, - {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, }; RegisterHandlers(functions); } -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index c557a2235..e9b4f76b2 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -6,13 +6,11 @@ #include "core/hle/service/vi/vi.h" -namespace Service { -namespace VI { +namespace Service::VI { class VI_U final : public Module::Interface { public: explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); }; -} // namespace VI -} // namespace Service +} // namespace Service::VI diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index ad3b56fcc..5f53b16d3 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <chrono> #include <mutex> #include <thread> @@ -87,7 +88,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); frame_limiting_delta_err = - MathUtil::Clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); + std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); if (frame_limiting_delta_err > microseconds::zero()) { std::this_thread::sleep_for(frame_limiting_delta_err); diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index 59a035e70..caffe48cb 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <chrono> #include <mutex> #include <thread> @@ -43,8 +44,8 @@ public: tilt_angle = 0; } else { tilt_direction = mouse_move.Cast<float>(); - tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f, - MathUtil::PI * 0.5f); + tilt_angle = + std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, MathUtil::PI * 0.5f); } } } diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index a710c4bc5..281810357 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(video_core STATIC engines/maxwell_3d.h engines/maxwell_compute.cpp engines/maxwell_compute.h + engines/shader_bytecode.h gpu.cpp gpu.h macro_interpreter.cpp @@ -27,6 +28,8 @@ add_library(video_core STATIC renderer_opengl/gl_shader_decompiler.h renderer_opengl/gl_shader_gen.cpp renderer_opengl/gl_shader_gen.h + renderer_opengl/gl_shader_manager.cpp + renderer_opengl/gl_shader_manager.h renderer_opengl/gl_shader_util.cpp renderer_opengl/gl_shader_util.h renderer_opengl/gl_state.cpp diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 2d7c3152f..2a3ff234a 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -74,8 +74,6 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { regs.reg_array[method] = value; -#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32)) - switch (method) { case MAXWELL3D_REG_INDEX(code_address.code_address_high): case MAXWELL3D_REG_INDEX(code_address.code_address_low): { @@ -136,7 +134,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { break; } -#undef MAXWELL3D_REG_INDEX + VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method); if (debug_context) { debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); @@ -165,6 +163,7 @@ void Maxwell3D::ProcessQueryGet() { void Maxwell3D::DrawArrays() { LOG_DEBUG(HW_GPU, "called, topology=%d, count=%d", regs.draw.topology.Value(), regs.vertex_buffer.count); + ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); @@ -176,7 +175,8 @@ void Maxwell3D::DrawArrays() { debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); } - VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(false /*is_indexed*/); + const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; + VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed); } void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { @@ -218,10 +218,12 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { Texture::TICEntry tic_entry; Memory::ReadBlock(tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); - ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear, - "TIC versions other than BlockLinear are unimplemented"); + ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || + tic_entry.header_version == Texture::TICHeaderVersion::Pitch, + "TIC versions other than BlockLinear or Pitch are unimplemented"); - ASSERT_MSG(tic_entry.texture_type == Texture::TextureType::Texture2D, + ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) || + (tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap), "Texture types other than Texture2D are unimplemented"); auto r_type = tic_entry.r_type.Value(); @@ -301,5 +303,26 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const { return regs.reg_array[method]; } +bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const { + // The Vertex stage is always enabled. + if (stage == Regs::ShaderStage::Vertex) + return true; + + switch (stage) { + case Regs::ShaderStage::TesselationControl: + return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::TesselationControl)] + .enable != 0; + case Regs::ShaderStage::TesselationEval: + return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::TesselationEval)] + .enable != 0; + case Regs::ShaderStage::Geometry: + return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::Geometry)].enable != 0; + case Regs::ShaderStage::Fragment: + return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::Fragment)].enable != 0; + } + + UNREACHABLE(); +} + } // namespace Engines } // namespace Tegra diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 98b39b2ff..d4fcedace 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -20,6 +20,9 @@ namespace Tegra { namespace Engines { +#define MAXWELL3D_REG_INDEX(field_name) \ + (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) + class Maxwell3D final { public: explicit Maxwell3D(MemoryManager& memory_manager); @@ -248,6 +251,52 @@ public: Patches = 0xe, }; + enum class IndexFormat : u32 { + UnsignedByte = 0x0, + UnsignedShort = 0x1, + UnsignedInt = 0x2, + }; + + struct Blend { + enum class Equation : u32 { + Add = 1, + Subtract = 2, + ReverseSubtract = 3, + Min = 4, + Max = 5, + }; + + enum class Factor : u32 { + Zero = 0x1, + One = 0x2, + SourceColor = 0x3, + OneMinusSourceColor = 0x4, + SourceAlpha = 0x5, + OneMinusSourceAlpha = 0x6, + DestAlpha = 0x7, + OneMinusDestAlpha = 0x8, + DestColor = 0x9, + OneMinusDestColor = 0xa, + SourceAlphaSaturate = 0xb, + Source1Color = 0x10, + OneMinusSource1Color = 0x11, + Source1Alpha = 0x12, + OneMinusSource1Alpha = 0x13, + ConstantColor = 0x61, + OneMinusConstantColor = 0x62, + ConstantAlpha = 0x63, + OneMinusConstantAlpha = 0x64, + }; + + u32 separate_alpha; + Equation equation_rgb; + Factor factor_source_rgb; + Factor factor_dest_rgb; + Equation equation_a; + Factor factor_source_a; + Factor factor_dest_a; + }; + union { struct { INSERT_PADDING_WORDS(0x200); @@ -270,7 +319,15 @@ public: } } rt[NumRenderTargets]; - INSERT_PADDING_WORDS(0x80); + struct { + f32 scale_x; + f32 scale_y; + f32 scale_z; + u32 translate_x; + u32 translate_y; + u32 translate_z; + INSERT_PADDING_WORDS(2); + } viewport_transform[NumViewports]; struct { union { @@ -375,7 +432,42 @@ public: }; } draw; - INSERT_PADDING_WORDS(0x139); + INSERT_PADDING_WORDS(0x6B); + + struct { + u32 start_addr_high; + u32 start_addr_low; + u32 end_addr_high; + u32 end_addr_low; + IndexFormat format; + u32 first; + u32 count; + + unsigned FormatSizeInBytes() const { + switch (format) { + case IndexFormat::UnsignedByte: + return 1; + case IndexFormat::UnsignedShort: + return 2; + case IndexFormat::UnsignedInt: + return 4; + } + UNREACHABLE(); + } + + GPUVAddr StartAddress() const { + return static_cast<GPUVAddr>( + (static_cast<GPUVAddr>(start_addr_high) << 32) | start_addr_low); + } + + GPUVAddr EndAddress() const { + return static_cast<GPUVAddr>((static_cast<GPUVAddr>(end_addr_high) << 32) | + end_addr_low); + } + } index_array; + + INSERT_PADDING_WORDS(0xC7); + struct { u32 query_address_high; u32 query_address_low; @@ -410,7 +502,9 @@ public: } } vertex_array[NumVertexArrays]; - INSERT_PADDING_WORDS(0x40); + Blend blend; + + INSERT_PADDING_WORDS(0x39); struct { u32 limit_high; @@ -427,14 +521,11 @@ public: BitField<0, 1, u32> enable; BitField<4, 4, ShaderProgram> program; }; - u32 start_id; - INSERT_PADDING_WORDS(1); - u32 gpr_alloc; - ShaderStage type; - INSERT_PADDING_WORDS(9); + u32 offset; + INSERT_PADDING_WORDS(14); } shader_config[MaxShaderProgram]; - INSERT_PADDING_WORDS(0x8C); + INSERT_PADDING_WORDS(0x80); struct { u32 cb_size; @@ -507,6 +598,7 @@ public: }; State state{}; + MemoryManager& memory_manager; /// Reads a register value located at the input method address u32 GetRegisterValue(u32 method) const; @@ -520,9 +612,10 @@ public: /// Returns a list of enabled textures for the specified shader stage. std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; -private: - MemoryManager& memory_manager; + /// Returns whether the specified shader stage is enabled or not. + bool IsShaderStageEnabled(Regs::ShaderStage stage) const; +private: std::unordered_map<u32, std::vector<u32>> uploaded_macros; /// Macro method that is currently being executed / being fed parameters. @@ -564,6 +657,7 @@ private: "Field " #field_name " has invalid position") ASSERT_REG_POSITION(rt, 0x200); +ASSERT_REG_POSITION(viewport_transform[0], 0x280); ASSERT_REG_POSITION(viewport, 0x300); ASSERT_REG_POSITION(vertex_buffer, 0x35D); ASSERT_REG_POSITION(zeta, 0x3F8); @@ -573,8 +667,10 @@ ASSERT_REG_POSITION(tsc, 0x557); ASSERT_REG_POSITION(tic, 0x55D); ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); +ASSERT_REG_POSITION(index_array, 0x5F2); ASSERT_REG_POSITION(query, 0x6C0); ASSERT_REG_POSITION(vertex_array[0], 0x700); +ASSERT_REG_POSITION(blend, 0x780); ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0); ASSERT_REG_POSITION(shader_config[0], 0x800); ASSERT_REG_POSITION(const_buffer, 0x8E0); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h new file mode 100644 index 000000000..5a006aee5 --- /dev/null +++ b/src/video_core/engines/shader_bytecode.h @@ -0,0 +1,439 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <bitset> +#include <cstring> +#include <map> +#include <string> +#include <vector> + +#include <boost/optional.hpp> + +#include "common/bit_field.h" +#include "common/common_types.h" + +namespace Tegra { +namespace Shader { + +struct Register { + // Register 255 is special cased to always be 0 + static constexpr size_t ZeroIndex = 255; + + constexpr Register() = default; + + constexpr Register(u64 value) : value(value) {} + + constexpr operator u64() const { + return value; + } + + template <typename T> + constexpr u64 operator-(const T& oth) const { + return value - oth; + } + + template <typename T> + constexpr u64 operator&(const T& oth) const { + return value & oth; + } + + constexpr u64 operator&(const Register& oth) const { + return value & oth.value; + } + + constexpr u64 operator~() const { + return ~value; + } + +private: + u64 value{}; +}; + +union Attribute { + Attribute() = default; + + constexpr explicit Attribute(u64 value) : value(value) {} + + enum class Index : u64 { + Position = 7, + Attribute_0 = 8, + }; + + union { + BitField<22, 2, u64> element; + BitField<24, 6, Index> index; + BitField<47, 3, u64> size; + } fmt20; + + union { + BitField<30, 2, u64> element; + BitField<32, 6, Index> index; + } fmt28; + + BitField<39, 8, u64> reg; + u64 value{}; +}; + +union Sampler { + Sampler() = default; + + constexpr explicit Sampler(u64 value) : value(value) {} + + enum class Index : u64 { + Sampler_0 = 8, + }; + + BitField<36, 13, Index> index; + u64 value{}; +}; + +union Uniform { + BitField<20, 14, u64> offset; + BitField<34, 5, u64> index; +}; + +} // namespace Shader +} // namespace Tegra + +namespace std { + +// TODO(bunnei): The below is forbidden by the C++ standard, but works fine. See #330. +template <> +struct make_unsigned<Tegra::Shader::Attribute> { + using type = Tegra::Shader::Attribute; +}; + +template <> +struct make_unsigned<Tegra::Shader::Register> { + using type = Tegra::Shader::Register; +}; + +} // namespace std + +namespace Tegra { +namespace Shader { + +enum class Pred : u64 { + UnusedIndex = 0x7, + NeverExecute = 0xF, +}; + +enum class PredCondition : u64 { + LessThan = 1, + Equal = 2, + LessEqual = 3, + GreaterThan = 4, + NotEqual = 5, + GreaterEqual = 6, + // TODO(Subv): Other condition types +}; + +enum class PredOperation : u64 { + And = 0, + Or = 1, + Xor = 2, +}; + +enum class SubOp : u64 { + Cos = 0x0, + Sin = 0x1, + Ex2 = 0x2, + Lg2 = 0x3, + Rcp = 0x4, + Rsq = 0x5, + Min = 0x8, +}; + +union Instruction { + Instruction& operator=(const Instruction& instr) { + value = instr.value; + return *this; + } + + constexpr Instruction(u64 value) : value{value} {} + + BitField<0, 8, Register> gpr0; + BitField<8, 8, Register> gpr8; + union { + BitField<16, 4, Pred> full_pred; + BitField<16, 3, u64> pred_index; + } pred; + BitField<19, 1, u64> negate_pred; + BitField<20, 8, Register> gpr20; + BitField<20, 7, SubOp> sub_op; + BitField<28, 8, Register> gpr28; + BitField<39, 8, Register> gpr39; + BitField<48, 16, u64> opcode; + + union { + BitField<20, 19, u64> imm20_19; + BitField<20, 32, u64> imm20_32; + BitField<45, 1, u64> negate_b; + BitField<46, 1, u64> abs_a; + BitField<48, 1, u64> negate_a; + BitField<49, 1, u64> abs_b; + BitField<50, 1, u64> abs_d; + BitField<56, 1, u64> negate_imm; + + float GetImm20_19() const { + float result{}; + u32 imm{static_cast<u32>(imm20_19)}; + imm <<= 12; + imm |= negate_imm ? 0x80000000 : 0; + std::memcpy(&result, &imm, sizeof(imm)); + return result; + } + + float GetImm20_32() const { + float result{}; + u32 imm{static_cast<u32>(imm20_32)}; + std::memcpy(&result, &imm, sizeof(imm)); + return result; + } + } alu; + + union { + BitField<48, 1, u64> negate_b; + BitField<49, 1, u64> negate_c; + } ffma; + + union { + BitField<0, 3, u64> pred0; + BitField<3, 3, u64> pred3; + BitField<7, 1, u64> abs_a; + BitField<39, 3, u64> pred39; + BitField<42, 1, u64> neg_pred; + BitField<43, 1, u64> neg_a; + BitField<44, 1, u64> abs_b; + BitField<45, 2, PredOperation> op; + BitField<47, 1, u64> ftz; + BitField<48, 4, PredCondition> cond; + BitField<56, 1, u64> neg_b; + } fsetp; + + BitField<61, 1, u64> is_b_imm; + BitField<60, 1, u64> is_b_gpr; + BitField<59, 1, u64> is_c_gpr; + + Attribute attribute; + Uniform uniform; + Sampler sampler; + + u64 value; +}; +static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size"); +static_assert(std::is_standard_layout<Instruction>::value, + "Structure does not have standard layout"); + +class OpCode { +public: + enum class Id { + KIL, + LD_A, + ST_A, + TEXQ, // Texture Query + TEXS, // Texture Fetch with scalar/non-vec4 source/destinations + TLDS, // Texture Load with scalar/non-vec4 source/destinations + EXIT, + IPA, + FFMA_IMM, // Fused Multiply and Add + FFMA_CR, + FFMA_RC, + FFMA_RR, + FADD_C, + FADD_R, + FADD_IMM, + FMUL_C, + FMUL_R, + FMUL_IMM, + FMUL32_IMM, + MUFU, // Multi-Function Operator + RRO, // Range Reduction Operator + F2F_C, + F2F_R, + F2F_IMM, + F2I_C, + F2I_R, + F2I_IMM, + I2F_C, + I2F_R, + I2F_IMM, + LOP32I, + MOV_C, + MOV_R, + MOV_IMM, + MOV32I, + SHR_C, + SHR_R, + SHR_IMM, + FSETP_C, // Set Predicate + FSETP_R, + FSETP_IMM, + ISETP_C, + ISETP_IMM, + ISETP_R, + }; + + enum class Type { + Trivial, + Arithmetic, + Ffma, + Flow, + Memory, + FloatPredicate, + IntegerPredicate, + Unknown, + }; + + class Matcher { + public: + Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type) + : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {} + + const char* GetName() const { + return name; + } + + u16 GetMask() const { + return mask; + } + + Id GetId() const { + return id; + } + + Type GetType() const { + return type; + } + + /** + * Tests to see if the given instruction is the instruction this matcher represents. + * @param instruction The instruction to test + * @returns true if the given instruction matches. + */ + bool Matches(u16 instruction) const { + return (instruction & mask) == expected; + } + + private: + const char* name; + u16 mask; + u16 expected; + Id id; + Type type; + }; + + static boost::optional<const Matcher&> Decode(Instruction instr) { + static const auto table{GetDecodeTable()}; + + const auto matches_instruction = [instr](const auto& matcher) { + return matcher.Matches(static_cast<u16>(instr.opcode)); + }; + + auto iter = std::find_if(table.begin(), table.end(), matches_instruction); + return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; + } + +private: + struct Detail { + private: + static constexpr size_t opcode_bitsize = 16; + + /** + * Generates the mask and the expected value after masking from a given bitstring. + * A '0' in a bitstring indicates that a zero must be present at that bit position. + * A '1' in a bitstring indicates that a one must be present at that bit position. + */ + static auto GetMaskAndExpect(const char* const bitstring) { + u16 mask = 0, expect = 0; + for (size_t i = 0; i < opcode_bitsize; i++) { + const size_t bit_position = opcode_bitsize - i - 1; + switch (bitstring[i]) { + case '0': + mask |= 1 << bit_position; + break; + case '1': + expect |= 1 << bit_position; + mask |= 1 << bit_position; + break; + default: + // Ignore + break; + } + } + return std::make_tuple(mask, expect); + } + + public: + /// Creates a matcher that can match and parse instructions based on bitstring. + static auto GetMatcher(const char* const bitstring, OpCode::Id op, OpCode::Type type, + const char* const name) { + const auto mask_expect = GetMaskAndExpect(bitstring); + return Matcher(name, std::get<0>(mask_expect), std::get<1>(mask_expect), op, type); + } + }; + + static std::vector<Matcher> GetDecodeTable() { + std::vector<Matcher> table = { +#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) + INST("111000110011----", Id::KIL, Type::Flow, "KIL"), + INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), + INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), + INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), + INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), + INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), + INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), + INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), + INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), + INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), + INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), + INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), + INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"), + INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"), + INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"), + INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), + INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), + INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), + INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"), + INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), + INST("0101110010010---", Id::RRO, Type::Arithmetic, "RRO"), + INST("0100110010101---", Id::F2F_C, Type::Arithmetic, "F2F_C"), + INST("0101110010101---", Id::F2F_R, Type::Arithmetic, "F2F_R"), + INST("0011100-10101---", Id::F2F_IMM, Type::Arithmetic, "F2F_IMM"), + INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"), + INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"), + INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"), + INST("0100110010111---", Id::I2F_C, Type::Arithmetic, "I2F_C"), + INST("0101110010111---", Id::I2F_R, Type::Arithmetic, "I2F_R"), + INST("0011100-10111---", Id::I2F_IMM, Type::Arithmetic, "I2F_IMM"), + INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"), + INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), + INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), + INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), + INST("000000010000----", Id::MOV32I, Type::Arithmetic, "MOV32I"), + INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"), + INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"), + INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"), + INST("010010111011----", Id::FSETP_C, Type::FloatPredicate, "FSETP_C"), + INST("010110111011----", Id::FSETP_R, Type::FloatPredicate, "FSETP_R"), + INST("0011011-1011----", Id::FSETP_IMM, Type::FloatPredicate, "FSETP_IMM"), + INST("010010110110----", Id::ISETP_C, Type::IntegerPredicate, "ISETP_C"), + INST("010110110110----", Id::ISETP_R, Type::IntegerPredicate, "ISETP_R"), + INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerPredicate, "ISETP_IMM"), + }; +#undef INST + std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { + // If a matcher has more bits in its mask it is more specific, so it + // should come first. + return std::bitset<16>(a.GetMask()).count() > std::bitset<16>(b.GetMask()).count(); + }); + + return table; + } +}; + +} // namespace Shader +} // namespace Tegra diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 71a8661b4..2888daedc 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -15,7 +15,10 @@ namespace Tegra { enum class RenderTargetFormat : u32 { NONE = 0x0, + RGBA16_FLOAT = 0xCA, + RGB10_A2_UNORM = 0xD1, RGBA8_UNORM = 0xD5, + RGBA8_SRGB = 0xD6, }; class DebugContext; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 35d262189..36629dd11 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -19,7 +19,7 @@ public: virtual void DrawArrays() = 0; /// Notify rasterizer that the specified Maxwell register has been changed - virtual void NotifyMaxwellRegisterChanged(u32 id) = 0; + virtual void NotifyMaxwellRegisterChanged(u32 method) = 0; /// Notify rasterizer that all caches should be flushed to Switch memory virtual void FlushAll() = 0; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index f217a265b..2d4a0d6db 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <memory> #include <string> #include <tuple> @@ -13,7 +14,6 @@ #include "common/math_util.h" #include "common/microprofile.h" #include "common/scope_exit.h" -#include "common/vector_math.h" #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/settings.h" @@ -34,33 +34,7 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); -enum class UniformBindings : GLuint { Common, VS, FS }; - -static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding, - size_t expected_size) { - GLuint ub_index = glGetUniformBlockIndex(shader, name); - if (ub_index != GL_INVALID_INDEX) { - GLint ub_size = 0; - glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); - ASSERT_MSG(ub_size == expected_size, - "Uniform block size did not match! Got %d, expected %zu", - static_cast<int>(ub_size), expected_size); - glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); - } -} - -static void SetShaderUniformBlockBindings(GLuint shader) { - SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common, - sizeof(RasterizerOpenGL::UniformData)); - SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, - sizeof(RasterizerOpenGL::VSUniformData)); - SetShaderUniformBlockBinding(shader, "fs_config", UniformBindings::FS, - sizeof(RasterizerOpenGL::FSUniformData)); -} - RasterizerOpenGL::RasterizerOpenGL() { - shader_dirty = true; - has_ARB_buffer_storage = false; has_ARB_direct_state_access = false; has_ARB_separate_shader_objects = false; @@ -72,6 +46,14 @@ RasterizerOpenGL::RasterizerOpenGL() { state.texture_units[i].sampler = texture_samplers[i].sampler.handle; } + // Create SSBOs + for (size_t stage = 0; stage < ssbos.size(); ++stage) { + for (size_t buffer = 0; buffer < ssbos[stage].size(); ++buffer) { + ssbos[stage][buffer].Create(); + state.draw.const_buffers[stage][buffer].ssbo = ssbos[stage][buffer].handle; + } + } + GLint ext_num; glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); for (GLint i = 0; i < ext_num; i++) { @@ -88,6 +70,8 @@ RasterizerOpenGL::RasterizerOpenGL() { } } + ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); + // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 state.clip_distance[0] = true; @@ -102,36 +86,30 @@ RasterizerOpenGL::RasterizerOpenGL() { state.draw.uniform_buffer = uniform_buffer.handle; state.Apply(); - glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), nullptr, GL_STATIC_DRAW); - glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle); - - uniform_block_data.dirty = true; - // Create render framebuffer framebuffer.Create(); - if (has_ARB_separate_shader_objects) { - hw_vao.Create(); - hw_vao_enabled_attributes.fill(false); + hw_vao.Create(); + hw_vao_enabled_attributes.fill(false); - stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER); - stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2); - state.draw.vertex_buffer = stream_buffer->GetHandle(); + stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER); + stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2); + state.draw.vertex_buffer = stream_buffer->GetHandle(); - pipeline.Create(); - state.draw.program_pipeline = pipeline.handle; - state.draw.shader_program = 0; - state.draw.vertex_array = hw_vao.handle; - state.Apply(); + shader_program_manager = std::make_unique<GLShader::ProgramManager>(); + state.draw.shader_program = 0; + state.draw.vertex_array = hw_vao.handle; + state.Apply(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle()); - vs_uniform_buffer.Create(); - glBindBuffer(GL_UNIFORM_BUFFER, vs_uniform_buffer.handle); - glBufferData(GL_UNIFORM_BUFFER, sizeof(VSUniformData), nullptr, GL_STREAM_COPY); - glBindBufferBase(GL_UNIFORM_BUFFER, 1, vs_uniform_buffer.handle); - } else { - UNREACHABLE(); + for (unsigned index = 0; index < uniform_buffers.size(); ++index) { + auto& buffer = uniform_buffers[index]; + buffer.Create(); + glBindBuffer(GL_UNIFORM_BUFFER, buffer.handle); + glBufferData(GL_UNIFORM_BUFFER, sizeof(GLShader::MaxwellUniformData), nullptr, + GL_STREAM_COPY); + glBindBufferBase(GL_UNIFORM_BUFFER, index, buffer.handle); } accelerate_draw = AccelDraw::Disabled; @@ -149,17 +127,6 @@ RasterizerOpenGL::~RasterizerOpenGL() { } } -void RasterizerOpenGL::AnalyzeVertexArray(bool is_indexed) { - const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; - - if (is_indexed) { - UNREACHABLE(); - } - - // TODO(bunnei): Add support for 1+ vertex arrays - vs_input_size = regs.vertex_buffer.count * regs.vertex_array[0].stride; -} - void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_VAO); const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; @@ -171,6 +138,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { // TODO(bunnei): Add support for 1+ vertex arrays const auto& vertex_array{regs.vertex_array[0]}; + const auto& vertex_array_limit{regs.vertex_array_limit[0]}; ASSERT_MSG(vertex_array.enable, "vertex array 0 is disabled?"); ASSERT_MSG(!vertex_array.divisor, "vertex array 0 divisor is unimplemented!"); for (unsigned index = 1; index < Maxwell::NumVertexArrays; ++index) { @@ -183,6 +151,10 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { // to avoid OpenGL errors. for (unsigned index = 0; index < 16; ++index) { auto& attrib = regs.vertex_attrib_format[index]; + NGLOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", + index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), + attrib.offset.Value(), attrib.IsNormalized()); + glVertexAttribPointer(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), attrib.IsNormalized() ? GL_TRUE : GL_FALSE, vertex_array.stride, reinterpret_cast<GLvoid*>(buffer_offset + attrib.offset)); @@ -191,7 +163,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { } // Copy vertex array data - const u32 data_size{vertex_array.stride * regs.vertex_buffer.count}; + const u64 data_size{vertex_array_limit.LimitAddress() - vertex_array.StartAddress() + 1}; const VAddr data_addr{memory_manager->PhysicalToVirtualAddress(vertex_array.StartAddress())}; res_cache.FlushRegion(data_addr, data_size, nullptr); Memory::ReadBlock(data_addr, array_ptr, data_size); @@ -200,26 +172,89 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { buffer_offset += data_size; } -void RasterizerOpenGL::SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset) { - MICROPROFILE_SCOPE(OpenGL_VS); - LOG_CRITICAL(Render_OpenGL, "Emulated shaders are not supported! Using a passthrough shader."); - glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_shader->shader.handle); -} +void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset, size_t ptr_pos) { + // Helper function for uploading uniform data + const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { + if (has_ARB_direct_state_access) { + glCopyNamedBufferSubData(stream_buffer->GetHandle(), handle, offset, 0, size); + } else { + glBindBuffer(GL_COPY_WRITE_BUFFER, handle); + glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); + } + }; -void RasterizerOpenGL::SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset) { - MICROPROFILE_SCOPE(OpenGL_FS); - UNREACHABLE(); -} + auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); + ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!"); -bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { - if (!has_ARB_separate_shader_objects) { - UNREACHABLE(); - return false; + // Next available bindpoint to use when uploading the const buffers to the GLSL shaders. + u32 current_constbuffer_bindpoint = 0; + + for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { + ptr_pos += sizeof(GLShader::MaxwellUniformData); + + auto& shader_config = gpu.regs.shader_config[index]; + const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; + + const auto& stage = index - 1; // Stage indices are 0 - 5 + + const bool is_enabled = gpu.IsShaderStageEnabled(static_cast<Maxwell::ShaderStage>(stage)); + + // Skip stages that are not enabled + if (!is_enabled) { + continue; + } + + // Upload uniform data as one UBO per stage + const GLintptr ubo_offset = buffer_offset + static_cast<GLintptr>(ptr_pos); + copy_buffer(uniform_buffers[stage].handle, ubo_offset, + sizeof(GLShader::MaxwellUniformData)); + GLShader::MaxwellUniformData* ub_ptr = + reinterpret_cast<GLShader::MaxwellUniformData*>(&buffer_ptr[ptr_pos]); + ub_ptr->SetFromRegs(gpu.state.shader_stages[stage]); + + // Fetch program code from memory + GLShader::ProgramCode program_code; + const u64 gpu_address{gpu.regs.code_address.CodeAddress() + shader_config.offset}; + const VAddr cpu_address{gpu.memory_manager.PhysicalToVirtualAddress(gpu_address)}; + Memory::ReadBlock(cpu_address, program_code.data(), program_code.size() * sizeof(u64)); + GLShader::ShaderSetup setup{std::move(program_code)}; + + GLShader::ShaderEntries shader_resources; + + switch (program) { + case Maxwell::ShaderProgram::VertexB: { + GLShader::MaxwellVSConfig vs_config{setup}; + shader_resources = + shader_program_manager->UseProgrammableVertexShader(vs_config, setup); + break; + } + case Maxwell::ShaderProgram::Fragment: { + GLShader::MaxwellFSConfig fs_config{setup}; + shader_resources = + shader_program_manager->UseProgrammableFragmentShader(fs_config, setup); + break; + } + default: + LOG_CRITICAL(HW_GPU, "Unimplemented shader index=%d, enable=%d, offset=0x%08X", index, + shader_config.enable.Value(), shader_config.offset); + UNREACHABLE(); + } + + GLuint gl_stage_program = shader_program_manager->GetCurrentProgramStage( + static_cast<Maxwell::ShaderStage>(stage)); + + // Configure the const buffers for this shader stage. + current_constbuffer_bindpoint = + SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, + current_constbuffer_bindpoint, shader_resources.const_buffer_entries); } + shader_program_manager->UseTrivialGeometryShader(); +} + +bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; DrawArrays(); - return true; } @@ -255,18 +290,18 @@ void RasterizerOpenGL::DrawArrays() { : (depth_surface == nullptr ? 1u : depth_surface->res_scale); MathUtil::Rectangle<u32> draw_rect{ - static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.left) + - viewport_rect.left * res_scale, - surfaces_rect.left, surfaces_rect.right)), // Left - static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + - viewport_rect.top * res_scale, - surfaces_rect.bottom, surfaces_rect.top)), // Top - static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.left) + - viewport_rect.right * res_scale, - surfaces_rect.left, surfaces_rect.right)), // Right - static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + - viewport_rect.bottom * res_scale, - surfaces_rect.bottom, surfaces_rect.top))}; // Bottom + static_cast<u32>( + std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale, + surfaces_rect.left, surfaces_rect.right)), // Left + static_cast<u32>( + std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale, + surfaces_rect.bottom, surfaces_rect.top)), // Top + static_cast<u32>( + std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale, + surfaces_rect.left, surfaces_rect.right)), // Right + static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + + viewport_rect.bottom * res_scale, + surfaces_rect.bottom, surfaces_rect.top))}; // Bottom // Bind the framebuffer surfaces BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); @@ -280,18 +315,6 @@ void RasterizerOpenGL::DrawArrays() { // Sync and bind the texture surfaces BindTextures(); - // Sync and bind the shader - if (shader_dirty) { - SetShader(); - shader_dirty = false; - } - - // Sync the uniform data - if (uniform_block_data.dirty) { - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformData), &uniform_block_data.data); - uniform_block_data.dirty = false; - } - // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable // scissor test to prevent drawing outside of the framebuffer region state.scissor.enabled = true; @@ -303,15 +326,22 @@ void RasterizerOpenGL::DrawArrays() { // Draw the vertex batch const bool is_indexed = accelerate_draw == AccelDraw::Indexed; - AnalyzeVertexArray(is_indexed); + const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; + const unsigned vertex_num{is_indexed ? regs.index_array.count : regs.vertex_buffer.count}; + + // TODO(bunnei): Add support for 1+ vertex arrays + vs_input_size = vertex_num * regs.vertex_array[0].stride; + state.draw.vertex_buffer = stream_buffer->GetHandle(); state.Apply(); size_t buffer_size = static_cast<size_t>(vs_input_size); if (is_indexed) { - UNREACHABLE(); + buffer_size = Common::AlignUp(buffer_size, 4) + index_buffer_size; } - buffer_size += sizeof(VSUniformData); + + // Uniform space for the 5 shader stages + buffer_size += sizeof(GLShader::MaxwellUniformData) * Maxwell::MaxShaderStage; size_t ptr_pos = 0; u8* buffer_ptr; @@ -322,36 +352,37 @@ void RasterizerOpenGL::DrawArrays() { SetupVertexArray(buffer_ptr, buffer_offset); ptr_pos += vs_input_size; + // If indexed mode, copy the index buffer GLintptr index_buffer_offset = 0; if (is_indexed) { - UNREACHABLE(); - } + ptr_pos = Common::AlignUp(ptr_pos, 4); - SetupVertexShader(reinterpret_cast<VSUniformData*>(&buffer_ptr[ptr_pos]), - buffer_offset + static_cast<GLintptr>(ptr_pos)); - const GLintptr vs_ubo_offset = buffer_offset + static_cast<GLintptr>(ptr_pos); - ptr_pos += sizeof(VSUniformData); + const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager; + const VAddr index_data_addr{ + memory_manager->PhysicalToVirtualAddress(regs.index_array.StartAddress())}; + Memory::ReadBlock(index_data_addr, &buffer_ptr[ptr_pos], index_buffer_size); - stream_buffer->Unmap(); + index_buffer_offset = buffer_offset + static_cast<GLintptr>(ptr_pos); + ptr_pos += index_buffer_size; + } - const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { - if (has_ARB_direct_state_access) { - glCopyNamedBufferSubData(stream_buffer->GetHandle(), handle, offset, 0, size); - } else { - glBindBuffer(GL_COPY_WRITE_BUFFER, handle); - glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); - } - }; + SetupShaders(buffer_ptr, buffer_offset, ptr_pos); - copy_buffer(vs_uniform_buffer.handle, vs_ubo_offset, sizeof(VSUniformData)); + stream_buffer->Unmap(); - glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_shader->shader.handle); + shader_program_manager->ApplyTo(state); + state.Apply(); + const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)}; if (is_indexed) { - UNREACHABLE(); + const GLint index_min{static_cast<GLint>(regs.index_array.first)}; + const GLint index_max{static_cast<GLint>(regs.index_array.first + regs.index_array.count)}; + glDrawRangeElementsBaseVertex(primitive_mode, index_min, index_max, regs.index_array.count, + MaxwellToGL::IndexFormat(regs.index_array.format), + reinterpret_cast<const void*>(index_buffer_offset), + -index_min); } else { - glDrawArrays(MaxwellToGL::PrimitiveTopology(regs.draw.topology), 0, - regs.vertex_buffer.count); + glDrawArrays(primitive_mode, 0, regs.vertex_buffer.count); } // Disable scissor test @@ -384,7 +415,7 @@ void RasterizerOpenGL::DrawArrays() { void RasterizerOpenGL::BindTextures() { using Regs = Tegra::Engines::Maxwell3D::Regs; - auto maxwell3d = Core::System::GetInstance().GPU().Get3DEngine(); + auto& maxwell3d = Core::System::GetInstance().GPU().Get3DEngine(); // Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a // certain number in OpenGL. We try to only use the minimum amount of host textures by not @@ -415,7 +446,32 @@ void RasterizerOpenGL::BindTextures() { } } -void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {} +void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + switch (method) { + case MAXWELL3D_REG_INDEX(blend.separate_alpha): + ASSERT_MSG(false, "unimplemented"); + break; + case MAXWELL3D_REG_INDEX(blend.equation_rgb): + state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb); + break; + case MAXWELL3D_REG_INDEX(blend.factor_source_rgb): + state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb); + break; + case MAXWELL3D_REG_INDEX(blend.factor_dest_rgb): + state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb); + break; + case MAXWELL3D_REG_INDEX(blend.equation_a): + state.blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a); + break; + case MAXWELL3D_REG_INDEX(blend.factor_source_a): + state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a); + break; + case MAXWELL3D_REG_INDEX(blend.factor_dest_a): + state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a); + break; + } +} void RasterizerOpenGL::FlushAll() { MICROPROFILE_SCOPE(OpenGL_CacheManagement); @@ -467,9 +523,12 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebu src_params.width = std::min(framebuffer.width, pixel_stride); src_params.height = framebuffer.height; src_params.stride = pixel_stride; - src_params.is_tiled = false; + src_params.is_tiled = true; + src_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; src_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format); + src_params.component_type = + SurfaceParams::ComponentTypeFromGPUPixelFormat(framebuffer.pixel_format); src_params.UpdateParams(); MathUtil::Rectangle<u32> src_rect; @@ -531,70 +590,53 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr } } -void RasterizerOpenGL::SetShader() { - // TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to - // OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell - // shaders. - - static constexpr char vertex_shader[] = R"( -#version 150 core - -in vec2 vert_position; -in vec2 vert_tex_coord; -out vec2 frag_tex_coord; - -void main() { - // Multiply input position by the rotscale part of the matrix and then manually translate by - // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector - // to `vec3(vert_position.xy, 1.0)` - gl_Position = vec4(mat2(mat3x2(0.0015625f, 0.0, 0.0, -0.0027778, -1.0, 1.0)) * vert_position + mat3x2(0.0015625f, 0.0, 0.0, -0.0027778, -1.0, 1.0)[2], 0.0, 1.0); - frag_tex_coord = vert_tex_coord; -} -)"; - - static constexpr char fragment_shader[] = R"( -#version 150 core - -in vec2 frag_tex_coord; -out vec4 color; - -uniform sampler2D tex[32]; +u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program, + u32 current_bindpoint, + const std::vector<GLShader::ConstBufferEntry>& entries) { + auto& gpu = Core::System::GetInstance().GPU(); + auto& maxwell3d = gpu.Get3DEngine(); -void main() { - color = texture(tex[0], frag_tex_coord); -} -)"; + ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage), + "Attempted to upload constbuffer of disabled shader stage"); - if (current_shader) { - return; + // Reset all buffer draw state for this stage. + for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) { + buffer.bindpoint = 0; + buffer.enabled = false; } - LOG_CRITICAL(Render_OpenGL, "Emulated shaders are not supported! Using a passthrough shader."); - - current_shader = &test_shader; - if (has_ARB_separate_shader_objects) { - test_shader.shader.Create(vertex_shader, nullptr, fragment_shader, {}, true); - glActiveShaderProgram(pipeline.handle, test_shader.shader.handle); - } else { - UNREACHABLE(); + // Upload only the enabled buffers from the 16 constbuffers of each shader stage + auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; + + for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { + const auto& used_buffer = entries[bindpoint]; + const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; + auto& buffer_draw_state = + state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()]; + + ASSERT_MSG(buffer.enabled, "Attempted to upload disabled constbuffer"); + buffer_draw_state.enabled = true; + buffer_draw_state.bindpoint = current_bindpoint + bindpoint; + + VAddr addr = gpu.memory_manager->PhysicalToVirtualAddress(buffer.address); + std::vector<u8> data(used_buffer.GetSize() * sizeof(float)); + Memory::ReadBlock(addr, data.data(), data.size()); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo); + glBufferData(GL_SHADER_STORAGE_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + // Now configure the bindpoint of the buffer inside the shader + std::string buffer_name = used_buffer.GetName(); + GLuint index = + glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, buffer_name.c_str()); + if (index != -1) + glShaderStorageBlockBinding(program, index, buffer_draw_state.bindpoint); } - state.draw.shader_program = test_shader.shader.handle; state.Apply(); - for (u32 texture = 0; texture < texture_samplers.size(); ++texture) { - // Set the texture samplers to correspond to different texture units - std::string uniform_name = "tex[" + std::to_string(texture) + "]"; - GLint uniform_tex = glGetUniformLocation(test_shader.shader.handle, uniform_name.c_str()); - if (uniform_tex != -1) { - glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id); - } - } - - if (has_ARB_separate_shader_objects) { - state.draw.shader_program = 0; - state.Apply(); - } + return current_bindpoint + entries.size(); } void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d868bf421..03e02b52a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -6,19 +6,16 @@ #include <array> #include <cstddef> -#include <cstring> #include <memory> -#include <unordered_map> #include <vector> #include <glad/glad.h> -#include "common/bit_field.h" #include "common/common_types.h" -#include "common/hash.h" -#include "common/vector_math.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" @@ -30,7 +27,7 @@ public: ~RasterizerOpenGL() override; void DrawArrays() override; - void NotifyMaxwellRegisterChanged(u32 id) override; + void NotifyMaxwellRegisterChanged(u32 method) override; void FlushAll() override; void FlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; @@ -45,7 +42,7 @@ public: /// OpenGL shader generated for a given Maxwell register state struct MaxwellShader { /// OpenGL shader resource - OGLShader shader; + OGLProgram shader; }; struct VertexShader { @@ -56,34 +53,6 @@ public: OGLShader shader; }; - /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned - // NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at - // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. - // Not following that rule will cause problems on some AMD drivers. - struct UniformData {}; - - // static_assert( - // sizeof(UniformData) == 0x460, - // "The size of the UniformData structure has changed, update the structure in the shader"); - static_assert(sizeof(UniformData) < 16384, - "UniformData structure must be less than 16kb as per the OpenGL spec"); - - struct VSUniformData {}; - // static_assert( - // sizeof(VSUniformData) == 1856, - // "The size of the VSUniformData structure has changed, update the structure in the - // shader"); - static_assert(sizeof(VSUniformData) < 16384, - "VSUniformData structure must be less than 16kb as per the OpenGL spec"); - - struct FSUniformData {}; - // static_assert( - // sizeof(FSUniformData) == 1856, - // "The size of the FSUniformData structure has changed, update the structure in the - // shader"); - static_assert(sizeof(FSUniformData) < 16384, - "FSUniformData structure must be less than 16kb as per the OpenGL spec"); - private: class SamplerInfo { public: @@ -113,6 +82,18 @@ private: /// Binds the required textures to OpenGL before drawing a batch. void BindTextures(); + /* + * Configures the current constbuffers to use for the draw command. + * @param stage The shader stage to configure buffers for. + * @param program The OpenGL program object that contains the specified stage. + * @param current_bindpoint The offset at which to start counting new buffer bindpoints. + * @param entries Vector describing the buffers that are actually used in the guest shader. + * @returns The next available bindpoint for use in the next shader stage. + */ + u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program, + u32 current_bindpoint, + const std::vector<GLShader::ConstBufferEntry>& entries); + /// Syncs the viewport to match the guest state void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); @@ -122,9 +103,6 @@ private: /// Syncs the clip coefficients to match the guest state void SyncClipCoef(); - /// Sets the OpenGL shader in accordance with the current guest state - void SetShader(); - /// Syncs the cull mode to match the guest state void SyncCullMode(); @@ -152,23 +130,16 @@ private: RasterizerCacheOpenGL res_cache; - /// Shader used for test renderering - to be removed once we have emulated shaders - MaxwellShader test_shader{}; - - const MaxwellShader* current_shader{}; - bool shader_dirty{}; - - struct { - UniformData data; - bool dirty; - } uniform_block_data = {}; - - OGLPipeline pipeline; + std::unique_ptr<GLShader::ProgramManager> shader_program_manager; OGLVertexArray sw_vao; OGLVertexArray hw_vao; std::array<bool, 16> hw_vao_enabled_attributes; - std::array<SamplerInfo, 32> texture_samplers; + std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; + std::array<std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers>, + Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> + ssbos; + static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024; std::unique_ptr<OGLStreamBuffer> vertex_buffer; OGLBuffer uniform_buffer; @@ -179,22 +150,11 @@ private: GLsizeiptr vs_input_size; - void AnalyzeVertexArray(bool is_indexed); void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset); - OGLBuffer vs_uniform_buffer; - std::unordered_map<GLShader::MaxwellVSConfig, VertexShader*> vs_shader_map; - std::unordered_map<std::string, VertexShader> vs_shader_cache; - OGLShader vs_default_shader; - - void SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset); - - OGLBuffer fs_uniform_buffer; - std::unordered_map<GLShader::MaxwellFSConfig, FragmentShader*> fs_shader_map; - std::unordered_map<std::string, FragmentShader> fs_shader_cache; - OGLShader fs_default_shader; + std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> uniform_buffers; - void SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset); + void SetupShaders(u8* buffer_ptr, GLintptr buffer_offset, size_t ptr_pos); enum class AccelDraw { Disabled, Arrays, Indexed }; AccelDraw accelerate_draw; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 5cbafa2e7..ced2b8247 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -7,7 +7,6 @@ #include <cstring> #include <iterator> #include <memory> -#include <unordered_set> #include <utility> #include <vector> #include <boost/optional.hpp> @@ -20,7 +19,6 @@ #include "common/math_util.h" #include "common/microprofile.h" #include "common/scope_exit.h" -#include "common/vector_math.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/hle/kernel/process.h" @@ -36,6 +34,7 @@ using SurfaceType = SurfaceParams::SurfaceType; using PixelFormat = SurfaceParams::PixelFormat; +using ComponentType = SurfaceParams::ComponentType; struct FormatTuple { GLint internal_format; @@ -47,26 +46,24 @@ struct FormatTuple { u32 compression_factor; }; -static constexpr std::array<FormatTuple, 1> fb_format_tuples = {{ - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8 +static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false, 1}, // ABGR8 + {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false, 1}, // B5G6R5 + {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1 + {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT23 + {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT45 }}; -static constexpr std::array<FormatTuple, 2> tex_format_tuples = {{ - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8 - {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1 -}}; - -static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { +static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); - if (type == SurfaceType::Color) { - ASSERT(static_cast<size_t>(pixel_format) < fb_format_tuples.size()); - return fb_format_tuples[static_cast<unsigned int>(pixel_format)]; + if (type == SurfaceType::ColorTexture) { + ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); + // For now only UNORM components are supported + ASSERT(component_type == ComponentType::UNorm); + return tex_format_tuples[static_cast<unsigned int>(pixel_format)]; } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { // TODO(Subv): Implement depth formats ASSERT_MSG(false, "Unimplemented"); - } else if (type == SurfaceType::Texture) { - ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); - return tex_format_tuples[static_cast<unsigned int>(pixel_format)]; } UNREACHABLE(); @@ -85,56 +82,42 @@ static u16 GetResolutionScaleFactor() { } template <bool morton_to_gl, PixelFormat format> -static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) { - constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8; - constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); - for (u32 y = 0; y < 8; ++y) { - for (u32 x = 0; x < 8; ++x) { - u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel; - u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel; - if (morton_to_gl) { - std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel); - } else { - std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel); - } - } - } -} - -template <bool morton_to_gl, PixelFormat format> -void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) { +void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, VAddr base, VAddr start, + VAddr end) { constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8; constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); - // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check the - // configuration for this and perform more generic un/swizzle - LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); - VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, - Memory::GetPointer(base), gl_buffer, morton_to_gl); -} - -template <> -void MortonCopy<true, PixelFormat::DXT1>(u32 stride, u32 height, u8* gl_buffer, VAddr base, - VAddr start, VAddr end) { - constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(PixelFormat::DXT1) / 8; - constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(PixelFormat::DXT1); - - // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check the - // configuration for this and perform more generic un/swizzle - LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); - auto data = - Tegra::Texture::UnswizzleTexture(base, Tegra::Texture::TextureFormat::DXT1, stride, height); - std::memcpy(gl_buffer, data.data(), data.size()); + if (morton_to_gl) { + auto data = Tegra::Texture::UnswizzleTexture( + base, SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, + block_height); + std::memcpy(gl_buffer, data.data(), data.size()); + } else { + // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check + // the configuration for this and perform more generic un/swizzle + LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); + VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, + Memory::GetPointer(base), gl_buffer, morton_to_gl); + } } -static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> morton_to_gl_fns = { - MortonCopy<true, PixelFormat::RGBA8>, - MortonCopy<true, PixelFormat::DXT1>, +static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr), + SurfaceParams::MaxPixelFormat> + morton_to_gl_fns = { + MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, + MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, + MortonCopy<true, PixelFormat::DXT45>, }; -static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> gl_to_morton_fns = { - MortonCopy<false, PixelFormat::RGBA8>, - MortonCopy<false, PixelFormat::DXT1>, +static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr), + SurfaceParams::MaxPixelFormat> + gl_to_morton_fns = { + MortonCopy<false, PixelFormat::ABGR8>, + MortonCopy<false, PixelFormat::B5G6R5>, + // TODO(Subv): Swizzling the DXT1/DXT23/DXT45 formats is not yet supported + nullptr, + nullptr, + nullptr, }; // Allocate an uninitialized texture of appropriate size and format for the surface @@ -183,7 +166,7 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec u32 buffers = 0; - if (type == SurfaceType::Color || type == SurfaceType::Texture) { + if (type == SurfaceType::ColorTexture) { glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex, 0); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, @@ -311,15 +294,18 @@ MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& su bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const { return std::tie(other_surface.addr, other_surface.width, other_surface.height, - other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) == - std::tie(addr, width, height, stride, pixel_format, is_tiled) && + other_surface.stride, other_surface.block_height, other_surface.pixel_format, + other_surface.component_type, + other_surface.is_tiled) == std::tie(addr, width, height, stride, block_height, + pixel_format, component_type, is_tiled) && pixel_format != PixelFormat::Invalid; } bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { return sub_surface.addr >= addr && sub_surface.end <= end && sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid && - sub_surface.is_tiled == is_tiled && + sub_surface.is_tiled == is_tiled && sub_surface.block_height == block_height && + sub_surface.component_type == component_type && (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) && GetSubRect(sub_surface).left + sub_surface.width <= stride; @@ -328,7 +314,8 @@ bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const { return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format && addr <= expanded_surface.end && expanded_surface.addr <= end && - is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride && + is_tiled == expanded_surface.is_tiled && block_height == expanded_surface.block_height && + component_type == expanded_surface.component_type && stride == expanded_surface.stride && (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) % BytesInPixels(stride * (is_tiled ? 8 : 1)) == 0; @@ -339,6 +326,10 @@ bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const { end < texcopy_params.end) { return false; } + if (texcopy_params.block_height != block_height || + texcopy_params.component_type != component_type) + return false; + if (texcopy_params.width != texcopy_params.stride) { const u32 tile_stride = static_cast<u32>(BytesInPixels(stride * (is_tiled ? 8 : 1))); return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && @@ -481,18 +472,13 @@ void CachedSurface::LoadGLBuffer(VAddr load_start, VAddr load_end) { const u64 start_offset = load_start - addr; if (!is_tiled) { - ASSERT(type == SurfaceType::Color); const u32 bytes_per_pixel{GetFormatBpp() >> 3}; - // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check - // the configuration for this and perform more generic un/swizzle - LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); - VideoCore::MortonCopyPixels128(width, height, bytes_per_pixel, 4, - texture_src_data + start_offset, &gl_buffer[start_offset], - true); + std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset, + bytes_per_pixel * width * height); } else { - morton_to_gl_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr, - load_start, load_end); + morton_to_gl_fns[static_cast<size_t>(pixel_format)]( + stride, block_height, height, &gl_buffer[0], addr, load_start, load_end); } } @@ -533,11 +519,10 @@ void CachedSurface::FlushGLBuffer(VAddr flush_start, VAddr flush_end) { if (backup_bytes) std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes); } else if (!is_tiled) { - ASSERT(type == SurfaceType::Color); std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start); } else { - gl_to_morton_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr, - flush_start, flush_end); + gl_to_morton_fns[static_cast<size_t>(pixel_format)]( + stride, block_height, height, &gl_buffer[0], addr, flush_start, flush_end); } } @@ -556,7 +541,7 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint GLint y0 = static_cast<GLint>(rect.bottom); size_t buffer_offset = (y0 * stride + x0) * GetGLBytesPerPixel(pixel_format); - const FormatTuple& tuple = GetFormatTuple(pixel_format); + const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type); GLuint target_tex = texture.handle; // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in @@ -629,7 +614,7 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui OpenGLState prev_state = state; SCOPE_EXIT({ prev_state.Apply(); }); - const FormatTuple& tuple = GetFormatTuple(pixel_format); + const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type); // Ensure no bad interactions with GL_PACK_ALIGNMENT ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0); @@ -662,7 +647,7 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui state.draw.read_framebuffer = read_fb_handle; state.Apply(); - if (type == SurfaceType::Color || type == SurfaceType::Texture) { + if (type == SurfaceType::ColorTexture) { glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle, 0); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, @@ -685,7 +670,8 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui glPixelStorei(GL_PACK_ROW_LENGTH, 0); } -enum MatchFlags { +enum class MatchFlags { + None = 0, Invalid = 1, // Flag that can be applied to other match types, invalid matches require // validation before they can be used Exact = 1 << 1, // Surfaces perfectly match @@ -699,6 +685,10 @@ constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) { return static_cast<MatchFlags>(static_cast<int>(lhs) | static_cast<int>(rhs)); } +constexpr MatchFlags operator&(MatchFlags lhs, MatchFlags rhs) { + return static_cast<MatchFlags>(static_cast<int>(lhs) & static_cast<int>(rhs)); +} + /// Get the best surface match (and its match type) for the given flags template <MatchFlags find_flags> Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params, @@ -716,15 +706,15 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params : (params.res_scale <= surface->res_scale); // validity will be checked in GetCopyableInterval bool is_valid = - find_flags & MatchFlags::Copy + (find_flags & MatchFlags::Copy) != MatchFlags::None ? true : surface->IsRegionValid(validate_interval.value_or(params.GetInterval())); - if (!(find_flags & MatchFlags::Invalid) && !is_valid) + if ((find_flags & MatchFlags::Invalid) == MatchFlags::None && !is_valid) continue; auto IsMatch_Helper = [&](auto check_type, auto match_fn) { - if (!(find_flags & check_type)) + if ((find_flags & check_type) == MatchFlags::None) return; bool matched; @@ -818,7 +808,7 @@ void main() { color = texelFetch(tbo, tbo_offset).rabg; } )"; - d24s8_abgr_shader.Create(vs_source, nullptr, fs_source); + d24s8_abgr_shader.CreateFromSource(vs_source, nullptr, fs_source); OpenGLState state = OpenGLState::GetCurState(); GLuint old_program = state.draw.shader_program; @@ -1041,9 +1031,25 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu params.height = config.tic.Height(); params.is_tiled = config.tic.IsTiled(); params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format); + + // TODO(Subv): Different types per component are not supported. + ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() && + config.tic.r_type.Value() == config.tic.b_type.Value() && + config.tic.r_type.Value() == config.tic.a_type.Value()); + + params.component_type = SurfaceParams::ComponentTypeFromTexture(config.tic.r_type.Value()); + + if (config.tic.IsTiled()) { + params.block_height = config.tic.BlockHeight(); + } else { + // Use the texture-provided stride value if the texture isn't tiled. + params.stride = params.PixelsInBytes(config.tic.Pitch()); + } + params.UpdateParams(); - if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0) { + if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0 || + params.stride != params.width) { Surface src_surface; MathUtil::Rectangle<u32> rect; std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); @@ -1083,10 +1089,10 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( } MathUtil::Rectangle<u32> viewport_clamped{ - static_cast<u32>(MathUtil::Clamp(viewport.left, 0, static_cast<s32>(config.width))), - static_cast<u32>(MathUtil::Clamp(viewport.top, 0, static_cast<s32>(config.height))), - static_cast<u32>(MathUtil::Clamp(viewport.right, 0, static_cast<s32>(config.width))), - static_cast<u32>(MathUtil::Clamp(viewport.bottom, 0, static_cast<s32>(config.height)))}; + static_cast<u32>(std::clamp(viewport.left, 0, static_cast<s32>(config.width))), + static_cast<u32>(std::clamp(viewport.top, 0, static_cast<s32>(config.height))), + static_cast<u32>(std::clamp(viewport.right, 0, static_cast<s32>(config.width))), + static_cast<u32>(std::clamp(viewport.bottom, 0, static_cast<s32>(config.height)))}; // get color and depth surfaces SurfaceParams color_params; @@ -1094,10 +1100,13 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( color_params.res_scale = resolution_scale_factor; color_params.width = config.width; color_params.height = config.height; + // TODO(Subv): Can framebuffers use a different block height? + color_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; SurfaceParams depth_params = color_params; color_params.addr = memory_manager->PhysicalToVirtualAddress(config.Address()); color_params.pixel_format = SurfaceParams::PixelFormatFromRenderTargetFormat(config.format); + color_params.component_type = SurfaceParams::ComponentTypeFromRenderTarget(config.format); color_params.UpdateParams(); ASSERT_MSG(!using_depth_fb, "depth buffer is unimplemented"); @@ -1293,7 +1302,6 @@ void RasterizerCacheOpenGL::InvalidateRegion(VAddr addr, u64 size, const Surface const SurfaceInterval invalid_interval(addr, addr + size); if (region_owner != nullptr) { - ASSERT(region_owner->type != SurfaceType::Texture); ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end); // Surfaces can't have a gap ASSERT(region_owner->width == region_owner->stride); @@ -1355,7 +1363,8 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { surface->gl_buffer_size = 0; surface->invalid_regions.insert(surface->GetInterval()); - AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format), + AllocateSurfaceTexture(surface->texture.handle, + GetFormatTuple(surface->pixel_format, surface->component_type), surface->GetScaledWidth(), surface->GetScaledHeight()); return surface; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 06524fc59..6861efe16 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -52,27 +52,45 @@ enum class ScaleMatch { struct SurfaceParams { enum class PixelFormat { - RGBA8 = 0, - DXT1 = 1, + ABGR8 = 0, + B5G6R5 = 1, + DXT1 = 2, + DXT23 = 3, + DXT45 = 4, + + Max, Invalid = 255, }; + static constexpr size_t MaxPixelFormat = static_cast<size_t>(PixelFormat::Max); + + enum class ComponentType { + Invalid = 0, + SNorm = 1, + UNorm = 2, + SInt = 3, + UInt = 4, + Float = 5, + }; + enum class SurfaceType { - Color = 0, - Texture = 1, - Depth = 2, - DepthStencil = 3, - Fill = 4, - Invalid = 5 + ColorTexture = 0, + Depth = 1, + DepthStencil = 2, + Fill = 3, + Invalid = 4, }; static constexpr unsigned int GetFormatBpp(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; - constexpr std::array<unsigned int, 2> bpp_table = { - 32, // RGBA8 - 64, // DXT1 + constexpr std::array<unsigned int, MaxPixelFormat> bpp_table = { + 32, // ABGR8 + 16, // B5G6R5 + 64, // DXT1 + 128, // DXT23 + 128, // DXT45 }; ASSERT(static_cast<size_t>(format) < bpp_table.size()); @@ -85,8 +103,9 @@ struct SurfaceParams { static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { switch (format) { case Tegra::RenderTargetFormat::RGBA8_UNORM: - return PixelFormat::RGBA8; + return PixelFormat::ABGR8; default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } @@ -94,8 +113,9 @@ struct SurfaceParams { static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { switch (format) { case Tegra::FramebufferConfig::PixelFormat::ABGR8: - return PixelFormat::RGBA8; + return PixelFormat::ABGR8; default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } @@ -104,10 +124,69 @@ struct SurfaceParams { // TODO(Subv): Properly implement this switch (format) { case Tegra::Texture::TextureFormat::A8R8G8B8: - return PixelFormat::RGBA8; + return PixelFormat::ABGR8; + case Tegra::Texture::TextureFormat::B5G6R5: + return PixelFormat::B5G6R5; case Tegra::Texture::TextureFormat::DXT1: return PixelFormat::DXT1; + case Tegra::Texture::TextureFormat::DXT23: + return PixelFormat::DXT23; + case Tegra::Texture::TextureFormat::DXT45: + return PixelFormat::DXT45; + default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } + } + + static Tegra::Texture::TextureFormat TextureFormatFromPixelFormat(PixelFormat format) { + // TODO(Subv): Properly implement this + switch (format) { + case PixelFormat::ABGR8: + return Tegra::Texture::TextureFormat::A8R8G8B8; + case PixelFormat::B5G6R5: + return Tegra::Texture::TextureFormat::B5G6R5; + case PixelFormat::DXT1: + return Tegra::Texture::TextureFormat::DXT1; + case PixelFormat::DXT23: + return Tegra::Texture::TextureFormat::DXT23; + case PixelFormat::DXT45: + return Tegra::Texture::TextureFormat::DXT45; + default: + UNREACHABLE(); + } + } + + static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { + // TODO(Subv): Implement more component types + switch (type) { + case Tegra::Texture::ComponentType::UNORM: + return ComponentType::UNorm; default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); + UNREACHABLE(); + } + } + + static ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { + // TODO(Subv): Implement more render targets + switch (format) { + case Tegra::RenderTargetFormat::RGBA8_UNORM: + case Tegra::RenderTargetFormat::RGB10_A2_UNORM: + return ComponentType::UNorm; + default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } + } + + static ComponentType ComponentTypeFromGPUPixelFormat( + Tegra::FramebufferConfig::PixelFormat format) { + switch (format) { + case Tegra::FramebufferConfig::PixelFormat::ABGR8: + return ComponentType::UNorm; + default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } @@ -116,8 +195,7 @@ struct SurfaceParams { SurfaceType a_type = GetFormatType(pixel_format_a); SurfaceType b_type = GetFormatType(pixel_format_b); - if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && - (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { + if (a_type == SurfaceType::ColorTexture && b_type == SurfaceType::ColorTexture) { return true; } @@ -133,12 +211,8 @@ struct SurfaceParams { } static SurfaceType GetFormatType(PixelFormat pixel_format) { - if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::RGBA8)) { - return SurfaceType::Color; - } - - if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::DXT1)) { - return SurfaceType::Texture; + if (static_cast<size_t>(pixel_format) < MaxPixelFormat) { + return SurfaceType::ColorTexture; } // TODO(Subv): Implement the other formats @@ -210,11 +284,13 @@ struct SurfaceParams { u32 width = 0; u32 height = 0; u32 stride = 0; + u32 block_height = 0; u16 res_scale = 1; bool is_tiled = false; PixelFormat pixel_format = PixelFormat::Invalid; SurfaceType type = SurfaceType::Invalid; + ComponentType component_type = ComponentType::Invalid; }; struct CachedSurface : SurfaceParams { @@ -334,7 +410,7 @@ private: OGLVertexArray attributeless_vao; OGLBuffer d24s8_abgr_buffer; GLsizeiptr d24s8_abgr_buffer_size; - OGLShader d24s8_abgr_shader; + OGLProgram d24s8_abgr_shader; GLint d24s8_abgr_tbo_size_u_id; GLint d24s8_abgr_viewport_u_id; }; diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 7da5e74d1..93f9172e7 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -13,14 +13,16 @@ class OGLTexture : private NonCopyable { public: OGLTexture() = default; - OGLTexture(OGLTexture&& o) { - std::swap(handle, o.handle); - } + + OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + ~OGLTexture() { Release(); } - OGLTexture& operator=(OGLTexture&& o) { - std::swap(handle, o.handle); + + OGLTexture& operator=(OGLTexture&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); return *this; } @@ -46,14 +48,16 @@ public: class OGLSampler : private NonCopyable { public: OGLSampler() = default; - OGLSampler(OGLSampler&& o) { - std::swap(handle, o.handle); - } + + OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + ~OGLSampler() { Release(); } - OGLSampler& operator=(OGLSampler&& o) { - std::swap(handle, o.handle); + + OGLSampler& operator=(OGLSampler&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); return *this; } @@ -79,25 +83,71 @@ public: class OGLShader : private NonCopyable { public: OGLShader() = default; - OGLShader(OGLShader&& o) { - std::swap(handle, o.handle); - } + + OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + ~OGLShader() { Release(); } - OGLShader& operator=(OGLShader&& o) { - std::swap(handle, o.handle); + + OGLShader& operator=(OGLShader&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); return *this; } - /// Creates a new internal OpenGL resource and stores the handle - void Create(const char* vert_shader, const char* geo_shader, const char* frag_shader, - const std::vector<const char*>& feedback_vars = {}, - bool separable_program = false) { + void Create(const char* source, GLenum type) { if (handle != 0) return; - handle = GLShader::LoadProgram(vert_shader, geo_shader, frag_shader, feedback_vars, - separable_program); + if (source == nullptr) + return; + handle = GLShader::LoadShader(source, type); + } + + void Release() { + if (handle == 0) + return; + glDeleteShader(handle); + handle = 0; + } + + GLuint handle = 0; +}; + +class OGLProgram : private NonCopyable { +public: + OGLProgram() = default; + + OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + + ~OGLProgram() { + Release(); + } + + OGLProgram& operator=(OGLProgram&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); + return *this; + } + + template <typename... T> + void Create(bool separable_program, T... shaders) { + if (handle != 0) + return; + handle = GLShader::LoadProgram(separable_program, shaders...); + } + + /// Creates a new internal OpenGL resource and stores the handle + void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader, + bool separable_program = false) { + OGLShader vert, geo, frag; + if (vert_shader) + vert.Create(vert_shader, GL_VERTEX_SHADER); + if (geo_shader) + geo.Create(geo_shader, GL_GEOMETRY_SHADER); + if (frag_shader) + frag.Create(frag_shader, GL_FRAGMENT_SHADER); + Create(separable_program, vert.handle, geo.handle, frag.handle); } /// Deletes the internal OpenGL resource @@ -115,13 +165,12 @@ public: class OGLPipeline : private NonCopyable { public: OGLPipeline() = default; - OGLPipeline(OGLPipeline&& o) { - handle = std::exchange<GLuint>(o.handle, 0); - } + OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {} + ~OGLPipeline() { Release(); } - OGLPipeline& operator=(OGLPipeline&& o) { + OGLPipeline& operator=(OGLPipeline&& o) noexcept { handle = std::exchange<GLuint>(o.handle, 0); return *this; } @@ -148,14 +197,16 @@ public: class OGLBuffer : private NonCopyable { public: OGLBuffer() = default; - OGLBuffer(OGLBuffer&& o) { - std::swap(handle, o.handle); - } + + OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + ~OGLBuffer() { Release(); } - OGLBuffer& operator=(OGLBuffer&& o) { - std::swap(handle, o.handle); + + OGLBuffer& operator=(OGLBuffer&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); return *this; } @@ -182,12 +233,12 @@ class OGLSync : private NonCopyable { public: OGLSync() = default; - OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {} + OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {} ~OGLSync() { Release(); } - OGLSync& operator=(OGLSync&& o) { + OGLSync& operator=(OGLSync&& o) noexcept { Release(); handle = std::exchange(o.handle, nullptr); return *this; @@ -214,14 +265,16 @@ public: class OGLVertexArray : private NonCopyable { public: OGLVertexArray() = default; - OGLVertexArray(OGLVertexArray&& o) { - std::swap(handle, o.handle); - } + + OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + ~OGLVertexArray() { Release(); } - OGLVertexArray& operator=(OGLVertexArray&& o) { - std::swap(handle, o.handle); + + OGLVertexArray& operator=(OGLVertexArray&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); return *this; } @@ -247,14 +300,16 @@ public: class OGLFramebuffer : private NonCopyable { public: OGLFramebuffer() = default; - OGLFramebuffer(OGLFramebuffer&& o) { - std::swap(handle, o.handle); - } + + OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} + ~OGLFramebuffer() { Release(); } - OGLFramebuffer& operator=(OGLFramebuffer&& o) { - std::swap(handle, o.handle); + + OGLFramebuffer& operator=(OGLFramebuffer&& o) noexcept { + Release(); + handle = std::exchange(o.handle, 0); return *this; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 564ea8f9e..086424395 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -2,57 +2,778 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <map> +#include <set> #include <string> -#include <queue> +#include <string_view> #include "common/assert.h" #include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" -namespace Maxwell3D { -namespace Shader { +namespace GLShader { namespace Decompiler { +using Tegra::Shader::Attribute; +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; +using Tegra::Shader::Register; +using Tegra::Shader::Sampler; +using Tegra::Shader::SubOp; +using Tegra::Shader::Uniform; + constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; -class Impl { +class DecompileFail : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +/// Describes the behaviour of code path of a given entry point and a return point. +enum class ExitMethod { + Undetermined, ///< Internal value. Only occur when analyzing JMP loop. + AlwaysReturn, ///< All code paths reach the return point. + Conditional, ///< Code path reaches the return point or an END instruction conditionally. + AlwaysEnd, ///< All code paths reach a END instruction. +}; + +/// A subroutine is a range of code refereced by a CALL, IF or LOOP instruction. +struct Subroutine { + /// Generates a name suitable for GLSL source code. + std::string GetName() const { + return "sub_" + std::to_string(begin) + "_" + std::to_string(end); + } + + u32 begin; ///< Entry point of the subroutine. + u32 end; ///< Return point of the subroutine. + ExitMethod exit_method; ///< Exit method of the subroutine. + std::set<u32> labels; ///< Addresses refereced by JMP instructions. + + bool operator<(const Subroutine& rhs) const { + return std::tie(begin, end) < std::tie(rhs.begin, rhs.end); + } +}; + +/// Analyzes shader code and produces a set of subroutines. +class ControlFlowAnalyzer { public: - Impl(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, - const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, u32 main_offset, - const std::function<std::string(u32)>& inputreg_getter, - const std::function<std::string(u32)>& outputreg_getter, bool sanitize_mul, - const std::string& emit_cb, const std::string& setemit_cb) - : program_code(program_code), swizzle_data(swizzle_data), main_offset(main_offset), - inputreg_getter(inputreg_getter), outputreg_getter(outputreg_getter), - sanitize_mul(sanitize_mul), emit_cb(emit_cb), setemit_cb(setemit_cb) {} + ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset) + : program_code(program_code) { + + // Recursively finds all subroutines. + const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END); + if (program_main.exit_method != ExitMethod::AlwaysEnd) + throw DecompileFail("Program does not always end"); + } - std::string Decompile() { - UNREACHABLE(); - return {}; + std::set<Subroutine> GetSubroutines() { + return std::move(subroutines); } private: - const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code; - const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data; - u32 main_offset; - const std::function<std::string(u32)>& inputreg_getter; - const std::function<std::string(u32)>& outputreg_getter; - bool sanitize_mul; - const std::string& emit_cb; - const std::string& setemit_cb; + const ProgramCode& program_code; + std::set<Subroutine> subroutines; + std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; + + /// Adds and analyzes a new subroutine if it is not added yet. + const Subroutine& AddSubroutine(u32 begin, u32 end) { + auto iter = subroutines.find(Subroutine{begin, end}); + if (iter != subroutines.end()) + return *iter; + + Subroutine subroutine{begin, end}; + subroutine.exit_method = Scan(begin, end, subroutine.labels); + if (subroutine.exit_method == ExitMethod::Undetermined) + throw DecompileFail("Recursive function detected"); + return *subroutines.insert(std::move(subroutine)).first; + } + + /// Scans a range of code for labels and determines the exit method. + ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) { + auto [iter, inserted] = + exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined); + ExitMethod& exit_method = iter->second; + if (!inserted) + return exit_method; + + for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { + if (const auto opcode = OpCode::Decode({program_code[offset]})) { + switch (opcode->GetId()) { + case OpCode::Id::EXIT: { + return exit_method = ExitMethod::AlwaysEnd; + } + } + } + } + return exit_method = ExitMethod::AlwaysReturn; + } +}; + +class ShaderWriter { +public: + void AddLine(std::string_view text) { + DEBUG_ASSERT(scope >= 0); + if (!text.empty()) { + AppendIndentation(); + } + shader_source += text; + AddNewLine(); + } + + void AddLine(char character) { + DEBUG_ASSERT(scope >= 0); + AppendIndentation(); + shader_source += character; + AddNewLine(); + } + + void AddNewLine() { + DEBUG_ASSERT(scope >= 0); + shader_source += '\n'; + } + + std::string GetResult() { + return std::move(shader_source); + } + + int scope = 0; + +private: + void AppendIndentation() { + shader_source.append(static_cast<size_t>(scope) * 4, ' '); + } + + std::string shader_source; }; -std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, - const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, - u32 main_offset, - const std::function<std::string(u32)>& inputreg_getter, - const std::function<std::string(u32)>& outputreg_getter, - bool sanitize_mul, const std::string& emit_cb, - const std::string& setemit_cb) { - Impl impl(program_code, swizzle_data, main_offset, inputreg_getter, outputreg_getter, - sanitize_mul, emit_cb, setemit_cb); - return impl.Decompile(); +class GLSLGenerator { +public: + GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, + u32 main_offset, Maxwell3D::Regs::ShaderStage stage) + : subroutines(subroutines), program_code(program_code), main_offset(main_offset), + stage(stage) { + + Generate(); + } + + std::string GetShaderCode() { + return declarations.GetResult() + shader.GetResult(); + } + + /// Returns entries in the shader that are useful for external functions + ShaderEntries GetEntries() const { + return {GetConstBuffersDeclarations()}; + } + +private: + /// Gets the Subroutine object corresponding to the specified address. + const Subroutine& GetSubroutine(u32 begin, u32 end) const { + auto iter = subroutines.find(Subroutine{begin, end}); + ASSERT(iter != subroutines.end()); + return *iter; + } + + /// Generates code representing an input attribute register. + std::string GetInputAttribute(Attribute::Index attribute) { + switch (attribute) { + case Attribute::Index::Position: + return "position"; + default: + const u32 index{static_cast<u32>(attribute) - + static_cast<u32>(Attribute::Index::Attribute_0)}; + if (attribute >= Attribute::Index::Attribute_0) { + declr_input_attribute.insert(attribute); + return "input_attribute_" + std::to_string(index); + } + + NGLOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); + UNREACHABLE(); + } + } + + /// Generates code representing an output attribute register. + std::string GetOutputAttribute(Attribute::Index attribute) { + switch (attribute) { + case Attribute::Index::Position: + return "position"; + default: + const u32 index{static_cast<u32>(attribute) - + static_cast<u32>(Attribute::Index::Attribute_0)}; + if (attribute >= Attribute::Index::Attribute_0) { + declr_output_attribute.insert(attribute); + return "output_attribute_" + std::to_string(index); + } + + NGLOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); + UNREACHABLE(); + } + } + + /// Generates code representing a 19-bit immediate value + static std::string GetImmediate19(const Instruction& instr) { + return std::to_string(instr.alu.GetImm20_19()); + } + + /// Generates code representing a 32-bit immediate value + static std::string GetImmediate32(const Instruction& instr) { + return std::to_string(instr.alu.GetImm20_32()); + } + + /// Generates code representing a temporary (GPR) register. + std::string GetRegister(const Register& reg, unsigned elem = 0) { + if (reg == Register::ZeroIndex) + return "0"; + if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) { + // GPRs 0-3 are output color for the fragment shader + return std::string{"color."} + "rgba"[(reg + elem) & 3]; + } + + return *declr_register.insert("register_" + std::to_string(reg + elem)).first; + } + + /// Generates code representing a uniform (C buffer) register. + std::string GetUniform(const Uniform& reg) { + declr_const_buffers[reg.index].MarkAsUsed(static_cast<unsigned>(reg.index), + static_cast<unsigned>(reg.offset), stage); + return 'c' + std::to_string(reg.index) + '[' + std::to_string(reg.offset) + ']'; + } + + /// Generates code representing a texture sampler. + std::string GetSampler(const Sampler& sampler) const { + // TODO(Subv): Support more than just texture sampler 0 + ASSERT_MSG(sampler.index == Sampler::Index::Sampler_0, "unsupported"); + const unsigned index{static_cast<unsigned>(sampler.index.Value()) - + static_cast<unsigned>(Sampler::Index::Sampler_0)}; + return "tex[" + std::to_string(index) + "]"; + } + + /** + * Adds code that calls a subroutine. + * @param subroutine the subroutine to call. + */ + void CallSubroutine(const Subroutine& subroutine) { + if (subroutine.exit_method == ExitMethod::AlwaysEnd) { + shader.AddLine(subroutine.GetName() + "();"); + shader.AddLine("return true;"); + } else if (subroutine.exit_method == ExitMethod::Conditional) { + shader.AddLine("if (" + subroutine.GetName() + "()) { return true; }"); + } else { + shader.AddLine(subroutine.GetName() + "();"); + } + } + + /** + * Writes code that does an assignment operation. + * @param reg the destination register code. + * @param value the code representing the value to assign. + */ + void SetDest(u64 elem, const std::string& reg, const std::string& value, + u64 dest_num_components, u64 value_num_components, bool is_abs = false) { + std::string swizzle = "."; + swizzle += "xyzw"[elem]; + + std::string dest = reg + (dest_num_components != 1 ? swizzle : ""); + std::string src = "(" + value + ")" + (value_num_components != 1 ? swizzle : ""); + src = is_abs ? "abs(" + src + ")" : src; + + shader.AddLine(dest + " = " + src + ";"); + } + + /* + * Writes code that assigns a predicate boolean variable. + * @param pred The id of the predicate to write to. + * @param value The expression value to assign to the predicate. + */ + void SetPredicate(u64 pred, const std::string& value) { + using Tegra::Shader::Pred; + // Can't assign to the constant predicate. + ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); + + std::string variable = 'p' + std::to_string(pred); + shader.AddLine(variable + " = " + value + ';'); + declr_predicates.insert(std::move(variable)); + } + + /* + * Returns the condition to use in the 'if' for a predicated instruction. + * @param instr Instruction to generate the if condition for. + * @returns string containing the predicate condition. + */ + std::string GetPredicateCondition(Instruction instr) const { + using Tegra::Shader::Pred; + ASSERT(instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)); + + std::string variable = + 'p' + std::to_string(static_cast<u64>(instr.pred.pred_index.Value())); + + if (instr.negate_pred) { + return "!(" + variable + ')'; + } + + return variable; + } + + /* + * Returns whether the instruction at the specified offset is a 'sched' instruction. + * Sched instructions always appear before a sequence of 3 instructions. + */ + bool IsSchedInstruction(u32 offset) const { + // sched instructions appear once every 4 instructions. + static constexpr size_t SchedPeriod = 4; + u32 absolute_offset = offset - main_offset; + + return (absolute_offset % SchedPeriod) == 0; + } + + /** + * Compiles a single instruction from Tegra to GLSL. + * @param offset the offset of the Tegra shader instruction. + * @return the offset of the next instruction to execute. Usually it is the current offset + * + 1. If the current instruction always terminates the program, returns PROGRAM_END. + */ + u32 CompileInstr(u32 offset) { + // Ignore sched instructions when generating code. + if (IsSchedInstruction(offset)) { + return offset + 1; + } + + const Instruction instr = {program_code[offset]}; + const auto opcode = OpCode::Decode(instr); + + // Decoding failure + if (!opcode) { + NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); + UNREACHABLE(); + } + + shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName()); + + using Tegra::Shader::Pred; + ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, + "NeverExecute predicate not implemented"); + + if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { + shader.AddLine("if (" + GetPredicateCondition(instr) + ')'); + shader.AddLine('{'); + ++shader.scope; + } + + switch (opcode->GetType()) { + case OpCode::Type::Arithmetic: { + std::string dest = GetRegister(instr.gpr0); + std::string op_a = instr.alu.negate_a ? "-" : ""; + op_a += GetRegister(instr.gpr8); + if (instr.alu.abs_a) { + op_a = "abs(" + op_a + ")"; + } + + std::string op_b = instr.alu.negate_b ? "-" : ""; + + if (instr.is_b_imm) { + op_b += GetImmediate19(instr); + } else { + if (instr.is_b_gpr) { + op_b += GetRegister(instr.gpr20); + } else { + op_b += GetUniform(instr.uniform); + } + } + + if (instr.alu.abs_b) { + op_b = "abs(" + op_b + ")"; + } + + switch (opcode->GetId()) { + case OpCode::Id::FMUL_C: + case OpCode::Id::FMUL_R: + case OpCode::Id::FMUL_IMM: { + SetDest(0, dest, op_a + " * " + op_b, 1, 1, instr.alu.abs_d); + break; + } + case OpCode::Id::FMUL32_IMM: { + // fmul32i doesn't have abs or neg bits. + SetDest(0, dest, GetRegister(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); + break; + } + case OpCode::Id::FADD_C: + case OpCode::Id::FADD_R: + case OpCode::Id::FADD_IMM: { + SetDest(0, dest, op_a + " + " + op_b, 1, 1, instr.alu.abs_d); + break; + } + case OpCode::Id::MUFU: { + switch (instr.sub_op) { + case SubOp::Cos: + SetDest(0, dest, "cos(" + op_a + ")", 1, 1, instr.alu.abs_d); + break; + case SubOp::Sin: + SetDest(0, dest, "sin(" + op_a + ")", 1, 1, instr.alu.abs_d); + break; + case SubOp::Ex2: + SetDest(0, dest, "exp2(" + op_a + ")", 1, 1, instr.alu.abs_d); + break; + case SubOp::Lg2: + SetDest(0, dest, "log2(" + op_a + ")", 1, 1, instr.alu.abs_d); + break; + case SubOp::Rcp: + SetDest(0, dest, "1.0 / " + op_a, 1, 1, instr.alu.abs_d); + break; + case SubOp::Rsq: + SetDest(0, dest, "inversesqrt(" + op_a + ")", 1, 1, instr.alu.abs_d); + break; + case SubOp::Min: + SetDest(0, dest, "min(" + op_a + "," + op_b + ")", 1, 1, instr.alu.abs_d); + break; + default: + NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", + static_cast<unsigned>(instr.sub_op.Value())); + UNREACHABLE(); + } + break; + } + case OpCode::Id::RRO: { + NGLOG_DEBUG(HW_GPU, "Skipping RRO instruction"); + break; + } + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + break; + } + case OpCode::Type::Ffma: { + std::string dest = GetRegister(instr.gpr0); + std::string op_a = GetRegister(instr.gpr8); + std::string op_b = instr.ffma.negate_b ? "-" : ""; + std::string op_c = instr.ffma.negate_c ? "-" : ""; + + switch (opcode->GetId()) { + case OpCode::Id::FFMA_CR: { + op_b += GetUniform(instr.uniform); + op_c += GetRegister(instr.gpr39); + break; + } + case OpCode::Id::FFMA_RR: { + op_b += GetRegister(instr.gpr20); + op_c += GetRegister(instr.gpr39); + break; + } + case OpCode::Id::FFMA_RC: { + op_b += GetRegister(instr.gpr39); + op_c += GetUniform(instr.uniform); + break; + } + case OpCode::Id::FFMA_IMM: { + op_b += GetImmediate19(instr); + op_c += GetRegister(instr.gpr39); + break; + } + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + + SetDest(0, dest, op_a + " * " + op_b + " + " + op_c, 1, 1); + break; + } + case OpCode::Type::Memory: { + std::string gpr0 = GetRegister(instr.gpr0); + const Attribute::Index attribute = instr.attribute.fmt20.index; + + switch (opcode->GetId()) { + case OpCode::Id::LD_A: { + ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); + SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); + break; + } + case OpCode::Id::ST_A: { + ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); + SetDest(instr.attribute.fmt20.element, GetOutputAttribute(attribute), gpr0, 4, 1); + break; + } + case OpCode::Id::TEXS: { + ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested"); + const std::string op_a = GetRegister(instr.gpr8); + const std::string op_b = GetRegister(instr.gpr20); + const std::string sampler = GetSampler(instr.sampler); + const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; + // Add an extra scope and declare the texture coords inside to prevent overwriting + // them in case they are used as outputs of the texs instruction. + shader.AddLine("{"); + ++shader.scope; + shader.AddLine(coord); + const std::string texture = "texture(" + sampler + ", coords)"; + for (unsigned elem = 0; elem < instr.attribute.fmt20.size; ++elem) { + SetDest(elem, GetRegister(instr.gpr0, elem), texture, 1, 4); + } + --shader.scope; + shader.AddLine("}"); + break; + } + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + break; + } + case OpCode::Type::FloatPredicate: { + std::string op_a = instr.fsetp.neg_a ? "-" : ""; + op_a += GetRegister(instr.gpr8); + + if (instr.fsetp.abs_a) { + op_a = "abs(" + op_a + ')'; + } + + std::string op_b{}; + + if (instr.is_b_imm) { + if (instr.fsetp.neg_b) { + // Only the immediate version of fsetp has a neg_b bit. + op_b += '-'; + } + op_b += '(' + GetImmediate19(instr) + ')'; + } else { + if (instr.is_b_gpr) { + op_b += GetRegister(instr.gpr20); + } else { + op_b += GetUniform(instr.uniform); + } + } + + if (instr.fsetp.abs_b) { + op_b = "abs(" + op_b + ')'; + } + + using Tegra::Shader::Pred; + ASSERT_MSG(instr.fsetp.pred0 == static_cast<u64>(Pred::UnusedIndex) && + instr.fsetp.pred39 == static_cast<u64>(Pred::UnusedIndex), + "Compound predicates are not implemented"); + + // We can't use the constant predicate as destination. + ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); + + using Tegra::Shader::PredCondition; + switch (instr.fsetp.cond) { + case PredCondition::LessThan: + SetPredicate(instr.fsetp.pred3, '(' + op_a + ") < (" + op_b + ')'); + break; + case PredCondition::Equal: + SetPredicate(instr.fsetp.pred3, '(' + op_a + ") == (" + op_b + ')'); + break; + default: + NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})", + static_cast<unsigned>(instr.fsetp.cond.Value()), op_a, op_b); + UNREACHABLE(); + } + break; + } + default: { + switch (opcode->GetId()) { + case OpCode::Id::EXIT: { + ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex), + "Predicated exits not implemented"); + shader.AddLine("return true;"); + offset = PROGRAM_END - 1; + break; + } + case OpCode::Id::KIL: { + shader.AddLine("discard;"); + break; + } + case OpCode::Id::IPA: { + const auto& attribute = instr.attribute.fmt28; + std::string dest = GetRegister(instr.gpr0); + SetDest(attribute.element, dest, GetInputAttribute(attribute.index), 1, 4); + break; + } + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + + break; + } + } + + // Close the predicate condition scope. + if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { + --shader.scope; + shader.AddLine('}'); + } + + return offset + 1; + } + + /** + * Compiles a range of instructions from Tegra to GLSL. + * @param begin the offset of the starting instruction. + * @param end the offset where the compilation should stop (exclusive). + * @return the offset of the next instruction to compile. PROGRAM_END if the program + * terminates. + */ + u32 CompileRange(u32 begin, u32 end) { + u32 program_counter; + for (program_counter = begin; program_counter < (begin > end ? PROGRAM_END : end);) { + program_counter = CompileInstr(program_counter); + } + return program_counter; + } + + void Generate() { + // Add declarations for all subroutines + for (const auto& subroutine : subroutines) { + shader.AddLine("bool " + subroutine.GetName() + "();"); + } + shader.AddNewLine(); + + // Add the main entry point + shader.AddLine("bool exec_shader() {"); + ++shader.scope; + CallSubroutine(GetSubroutine(main_offset, PROGRAM_END)); + --shader.scope; + shader.AddLine("}\n"); + + // Add definitions for all subroutines + for (const auto& subroutine : subroutines) { + std::set<u32> labels = subroutine.labels; + + shader.AddLine("bool " + subroutine.GetName() + "() {"); + ++shader.scope; + + if (labels.empty()) { + if (CompileRange(subroutine.begin, subroutine.end) != PROGRAM_END) { + shader.AddLine("return false;"); + } + } else { + labels.insert(subroutine.begin); + shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); + shader.AddLine("while (true) {"); + ++shader.scope; + + shader.AddLine("switch (jmp_to) {"); + + for (auto label : labels) { + shader.AddLine("case " + std::to_string(label) + "u: {"); + ++shader.scope; + + auto next_it = labels.lower_bound(label + 1); + u32 next_label = next_it == labels.end() ? subroutine.end : *next_it; + + u32 compile_end = CompileRange(label, next_label); + if (compile_end > next_label && compile_end != PROGRAM_END) { + // This happens only when there is a label inside a IF/LOOP block + shader.AddLine("{ jmp_to = " + std::to_string(compile_end) + "u; break; }"); + labels.emplace(compile_end); + } + + --shader.scope; + shader.AddLine('}'); + } + + shader.AddLine("default: return false;"); + shader.AddLine('}'); + + --shader.scope; + shader.AddLine('}'); + + shader.AddLine("return false;"); + } + + --shader.scope; + shader.AddLine("}\n"); + + DEBUG_ASSERT(shader.scope == 0); + } + + GenerateDeclarations(); + } + + /// Returns a list of constant buffer declarations + std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { + std::vector<ConstBufferEntry> result; + std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), + std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); }); + return result; + } + + /// Add declarations for registers + void GenerateDeclarations() { + for (const auto& reg : declr_register) { + declarations.AddLine("float " + reg + " = 0.0;"); + } + declarations.AddNewLine(); + + for (const auto& index : declr_input_attribute) { + // TODO(bunnei): Use proper number of elements for these + declarations.AddLine("layout(location = " + + std::to_string(static_cast<u32>(index) - + static_cast<u32>(Attribute::Index::Attribute_0)) + + ") in vec4 " + GetInputAttribute(index) + ";"); + } + declarations.AddNewLine(); + + for (const auto& index : declr_output_attribute) { + // TODO(bunnei): Use proper number of elements for these + declarations.AddLine("layout(location = " + + std::to_string(static_cast<u32>(index) - + static_cast<u32>(Attribute::Index::Attribute_0)) + + ") out vec4 " + GetOutputAttribute(index) + ";"); + } + declarations.AddNewLine(); + + unsigned const_buffer_layout = 0; + for (const auto& entry : GetConstBuffersDeclarations()) { + declarations.AddLine("layout(std430) buffer " + entry.GetName()); + declarations.AddLine('{'); + declarations.AddLine(" float c" + std::to_string(entry.GetIndex()) + "[];"); + declarations.AddLine("};"); + declarations.AddNewLine(); + ++const_buffer_layout; + } + + declarations.AddNewLine(); + for (const auto& pred : declr_predicates) { + declarations.AddLine("bool " + pred + " = false;"); + } + declarations.AddNewLine(); + } + +private: + const std::set<Subroutine>& subroutines; + const ProgramCode& program_code; + const u32 main_offset; + Maxwell3D::Regs::ShaderStage stage; + + ShaderWriter shader; + ShaderWriter declarations; + + // Declarations + std::set<std::string> declr_register; + std::set<std::string> declr_predicates; + std::set<Attribute::Index> declr_input_attribute; + std::set<Attribute::Index> declr_output_attribute; + std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; +}; // namespace Decompiler + +std::string GetCommonDeclarations() { + return "bool exec_shader();"; +} + +boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, + Maxwell3D::Regs::ShaderStage stage) { + try { + auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines(); + GLSLGenerator generator(subroutines, program_code, main_offset, stage); + return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; + } catch (const DecompileFail& exception) { + NGLOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); + } + return boost::none; } } // namespace Decompiler -} // namespace Shader -} // namespace Maxwell3D +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 02ebfcbe8..382c76b7a 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -2,26 +2,25 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <array> #include <functional> #include <string> +#include <boost/optional.hpp> #include "common/common_types.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_opengl/gl_shader_gen.h" -namespace Maxwell3D { -namespace Shader { +namespace GLShader { namespace Decompiler { -constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100000}; -constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100000}; +using Tegra::Engines::Maxwell3D; + +std::string GetCommonDeclarations(); -std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, - const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, - u32 main_offset, - const std::function<std::string(u32)>& inputreg_getter, - const std::function<std::string(u32)>& outputreg_getter, - bool sanitize_mul, const std::string& emit_cb = "", - const std::string& setemit_cb = ""); +boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, + Maxwell3D::Regs::ShaderStage stage); } // namespace Decompiler -} // namespace Shader -} // namespace Maxwell3D +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 8f3c98800..254f6e2c3 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -3,18 +3,74 @@ // Refer to the license.txt file included. #include "common/assert.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/renderer_opengl/gl_shader_gen.h" namespace GLShader { -std::string GenerateVertexShader(const MaxwellVSConfig& config) { - UNREACHABLE(); - return {}; +using Tegra::Engines::Maxwell3D; + +static constexpr u32 PROGRAM_OFFSET{10}; + +ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config) { + std::string out = "#version 430 core\n"; + out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; + out += Decompiler::GetCommonDeclarations(); + + ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET, + Maxwell3D::Regs::ShaderStage::Vertex) + .get_value_or({}); + out += R"( + +out gl_PerVertex { + vec4 gl_Position; +}; + +out vec4 position; + +layout (std140) uniform vs_config { + vec4 viewport_flip; +}; + +void main() { + exec_shader(); + + // Viewport can be flipped, which is unsupported by glViewport + position.xy *= viewport_flip.xy; + gl_Position = position; +} +)"; + out += program.first; + return {out, program.second}; +} + +ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config) { + std::string out = "#version 430 core\n"; + out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; + out += Decompiler::GetCommonDeclarations(); + + ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET, + Maxwell3D::Regs::ShaderStage::Fragment) + .get_value_or({}); + out += R"( + +in vec4 position; +out vec4 color; + +layout (std140) uniform fs_config { + vec4 viewport_flip; +}; + +uniform sampler2D tex[32]; + +void main() { + exec_shader(); } -std::string GenerateFragmentShader(const MaxwellFSConfig& config) { - UNREACHABLE(); - return {}; +)"; + out += program.first; + return {out, program.second}; } } // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 5101e7d30..458032b5c 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -4,46 +4,113 @@ #pragma once -#include <cstring> +#include <array> #include <string> #include <type_traits> +#include <utility> +#include <vector> +#include "common/common_types.h" #include "common/hash.h" namespace GLShader { -enum Attributes { - ATTRIBUTE_POSITION, - ATTRIBUTE_COLOR, - ATTRIBUTE_TEXCOORD0, - ATTRIBUTE_TEXCOORD1, - ATTRIBUTE_TEXCOORD2, - ATTRIBUTE_TEXCOORD0_W, - ATTRIBUTE_NORMQUAT, - ATTRIBUTE_VIEW, +constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; + +using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>; + +class ConstBufferEntry { + using Maxwell = Tegra::Engines::Maxwell3D::Regs; + +public: + void MarkAsUsed(unsigned index, unsigned offset, Maxwell::ShaderStage stage) { + is_used = true; + this->index = index; + this->stage = stage; + max_offset = std::max(max_offset, offset); + } + + bool IsUsed() const { + return is_used; + } + + unsigned GetIndex() const { + return index; + } + + unsigned GetSize() const { + return max_offset + 1; + } + + std::string GetName() const { + return BufferBaseNames[static_cast<size_t>(stage)] + std::to_string(index); + } + +private: + static constexpr std::array<const char*, Maxwell::MaxShaderStage> BufferBaseNames = { + "buffer_vs_c", "buffer_tessc_c", "buffer_tesse_c", "buffer_gs_c", "buffer_fs_c", + }; + + bool is_used{}; + unsigned index{}; + unsigned max_offset{}; + Maxwell::ShaderStage stage; }; -struct MaxwellShaderConfigCommon { - explicit MaxwellShaderConfigCommon(){}; +struct ShaderEntries { + std::vector<ConstBufferEntry> const_buffer_entries; }; -struct MaxwellVSConfig : MaxwellShaderConfigCommon { - explicit MaxwellVSConfig() : MaxwellShaderConfigCommon() {} +using ProgramResult = std::pair<std::string, ShaderEntries>; - bool operator==(const MaxwellVSConfig& o) const { - return std::memcmp(this, &o, sizeof(MaxwellVSConfig)) == 0; - }; +struct ShaderSetup { + ShaderSetup(ProgramCode&& program_code) : program_code(std::move(program_code)) {} + + ProgramCode program_code; + bool program_code_hash_dirty = true; + + u64 GetProgramCodeHash() { + if (program_code_hash_dirty) { + program_code_hash = Common::ComputeHash64(&program_code, sizeof(program_code)); + program_code_hash_dirty = false; + } + return program_code_hash; + } + +private: + u64 program_code_hash{}; }; -struct MaxwellFSConfig : MaxwellShaderConfigCommon { - explicit MaxwellFSConfig() : MaxwellShaderConfigCommon() {} +struct MaxwellShaderConfigCommon { + void Init(ShaderSetup& setup) { + program_hash = setup.GetProgramCodeHash(); + } - bool operator==(const MaxwellFSConfig& o) const { - return std::memcmp(this, &o, sizeof(MaxwellFSConfig)) == 0; - }; + u64 program_hash; }; -std::string GenerateVertexShader(const MaxwellVSConfig& config); -std::string GenerateFragmentShader(const MaxwellFSConfig& config); +struct MaxwellVSConfig : Common::HashableStruct<MaxwellShaderConfigCommon> { + explicit MaxwellVSConfig(ShaderSetup& setup) { + state.Init(setup); + } +}; + +struct MaxwellFSConfig : Common::HashableStruct<MaxwellShaderConfigCommon> { + explicit MaxwellFSConfig(ShaderSetup& setup) { + state.Init(setup); + } +}; + +/** + * Generates the GLSL vertex shader program source code for the given VS program + * @returns String of the shader source code + */ +ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config); + +/** + * Generates the GLSL fragment shader program source code for the given FS program + * @returns String of the shader source code + */ +ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config); } // namespace GLShader @@ -52,14 +119,14 @@ namespace std { template <> struct hash<GLShader::MaxwellVSConfig> { size_t operator()(const GLShader::MaxwellVSConfig& k) const { - return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellVSConfig)); + return k.Hash(); } }; template <> struct hash<GLShader::MaxwellFSConfig> { size_t operator()(const GLShader::MaxwellFSConfig& k) const { - return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellFSConfig)); + return k.Hash(); } }; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp new file mode 100644 index 000000000..17b3925a0 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/hle/kernel/process.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" + +namespace GLShader { + +namespace Impl { +void SetShaderUniformBlockBinding(GLuint shader, const char* name, + Maxwell3D::Regs::ShaderStage binding, size_t expected_size) { + GLuint ub_index = glGetUniformBlockIndex(shader, name); + if (ub_index != GL_INVALID_INDEX) { + GLint ub_size = 0; + glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); + ASSERT_MSG(ub_size == expected_size, + "Uniform block size did not match! Got %d, expected %zu", + static_cast<int>(ub_size), expected_size); + glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); + } +} + +void SetShaderUniformBlockBindings(GLuint shader) { + SetShaderUniformBlockBinding(shader, "vs_config", Maxwell3D::Regs::ShaderStage::Vertex, + sizeof(MaxwellUniformData)); + SetShaderUniformBlockBinding(shader, "gs_config", Maxwell3D::Regs::ShaderStage::Geometry, + sizeof(MaxwellUniformData)); + SetShaderUniformBlockBinding(shader, "fs_config", Maxwell3D::Regs::ShaderStage::Fragment, + sizeof(MaxwellUniformData)); +} + +void SetShaderSamplerBindings(GLuint shader) { + OpenGLState cur_state = OpenGLState::GetCurState(); + GLuint old_program = std::exchange(cur_state.draw.shader_program, shader); + cur_state.Apply(); + + // Set the texture samplers to correspond to different texture units + for (u32 texture = 0; texture < NumTextureSamplers; ++texture) { + // Set the texture samplers to correspond to different texture units + std::string uniform_name = "tex[" + std::to_string(texture) + "]"; + GLint uniform_tex = glGetUniformLocation(shader, uniform_name.c_str()); + if (uniform_tex != -1) { + glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id); + } + } + + cur_state.draw.shader_program = old_program; + cur_state.Apply(); +} + +} // namespace Impl + +void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + // TODO(bunnei): Support more than one viewport + viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0; + viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0; +} + +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h new file mode 100644 index 000000000..e963b4b7e --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -0,0 +1,175 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <tuple> +#include <unordered_map> +#include <boost/functional/hash.hpp> +#include <glad/glad.h> +#include "video_core/renderer_opengl/gl_resource_manager.h" +#include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/renderer_opengl/maxwell_to_gl.h" + +namespace GLShader { + +/// Number of OpenGL texture samplers that can be used in the fragment shader +static constexpr size_t NumTextureSamplers = 32; + +using Tegra::Engines::Maxwell3D; + +namespace Impl { +void SetShaderUniformBlockBindings(GLuint shader); +void SetShaderSamplerBindings(GLuint shader); +} // namespace Impl + +/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned +// NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at +// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. +// Not following that rule will cause problems on some AMD drivers. +struct MaxwellUniformData { + void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); + alignas(16) GLvec4 viewport_flip; +}; +static_assert(sizeof(MaxwellUniformData) == 16, "MaxwellUniformData structure size is incorrect"); +static_assert(sizeof(MaxwellUniformData) < 16384, + "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); + +class OGLShaderStage { +public: + OGLShaderStage() = default; + + void Create(const ProgramResult& program_result, GLenum type) { + OGLShader shader; + shader.Create(program_result.first.c_str(), type); + program.Create(true, shader.handle); + Impl::SetShaderUniformBlockBindings(program.handle); + Impl::SetShaderSamplerBindings(program.handle); + entries = program_result.second; + } + GLuint GetHandle() const { + return program.handle; + } + + ShaderEntries GetEntries() const { + return entries; + } + +private: + OGLProgram program; + ShaderEntries entries; +}; + +// TODO(wwylele): beautify this doc +// This is a shader cache designed for translating PICA shader to GLSL shader. +// The double cache is needed because diffent KeyConfigType, which includes a hash of the code +// region (including its leftover unused code) can generate the same GLSL code. +template <typename KeyConfigType, + ProgramResult (*CodeGenerator)(const ShaderSetup&, const KeyConfigType&), + GLenum ShaderType> +class ShaderCache { +public: + ShaderCache() = default; + + using Result = std::pair<GLuint, ShaderEntries>; + + Result Get(const KeyConfigType& key, const ShaderSetup& setup) { + auto map_it = shader_map.find(key); + if (map_it == shader_map.end()) { + ProgramResult program = CodeGenerator(setup, key); + + auto [iter, new_shader] = shader_cache.emplace(program.first, OGLShaderStage{}); + OGLShaderStage& cached_shader = iter->second; + if (new_shader) { + cached_shader.Create(program, ShaderType); + } + shader_map[key] = &cached_shader; + return {cached_shader.GetHandle(), program.second}; + } else { + return {map_it->second->GetHandle(), map_it->second->GetEntries()}; + } + } + +private: + std::unordered_map<KeyConfigType, OGLShaderStage*> shader_map; + std::unordered_map<std::string, OGLShaderStage> shader_cache; +}; + +using VertexShaders = ShaderCache<MaxwellVSConfig, &GenerateVertexShader, GL_VERTEX_SHADER>; + +using FragmentShaders = ShaderCache<MaxwellFSConfig, &GenerateFragmentShader, GL_FRAGMENT_SHADER>; + +class ProgramManager { +public: + ProgramManager() { + pipeline.Create(); + } + + ShaderEntries UseProgrammableVertexShader(const MaxwellVSConfig& config, + const ShaderSetup setup) { + ShaderEntries result; + std::tie(current.vs, result) = vertex_shaders.Get(config, setup); + return result; + } + + ShaderEntries UseProgrammableFragmentShader(const MaxwellFSConfig& config, + const ShaderSetup setup) { + ShaderEntries result; + std::tie(current.fs, result) = fragment_shaders.Get(config, setup); + return result; + } + + GLuint GetCurrentProgramStage(Maxwell3D::Regs::ShaderStage stage) { + switch (stage) { + case Maxwell3D::Regs::ShaderStage::Vertex: + return current.vs; + case Maxwell3D::Regs::ShaderStage::Fragment: + return current.fs; + } + + UNREACHABLE(); + } + + void UseTrivialGeometryShader() { + current.gs = 0; + } + + void ApplyTo(OpenGLState& state) { + // Workaround for AMD bug + glUseProgramStages(pipeline.handle, + GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, + 0); + + glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current.vs); + glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current.gs); + glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current.fs); + state.draw.shader_program = 0; + state.draw.program_pipeline = pipeline.handle; + } + +private: + struct ShaderTuple { + GLuint vs = 0, gs = 0, fs = 0; + bool operator==(const ShaderTuple& rhs) const { + return std::tie(vs, gs, fs) == std::tie(rhs.vs, rhs.gs, rhs.fs); + } + struct Hash { + std::size_t operator()(const ShaderTuple& tuple) const { + std::size_t hash = 0; + boost::hash_combine(hash, tuple.vs); + boost::hash_combine(hash, tuple.gs); + boost::hash_combine(hash, tuple.fs); + return hash; + } + }; + }; + ShaderTuple current; + VertexShaders vertex_shaders; + FragmentShaders fragment_shaders; + + std::unordered_map<ShaderTuple, OGLProgram, ShaderTuple::Hash> program_cache; + OGLPipeline pipeline; +}; + +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index a6c6204d5..8568fface 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -10,156 +10,41 @@ namespace GLShader { -GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader, - const char* fragment_shader, const std::vector<const char*>& feedback_vars, - bool separable_program) { - // Create the shaders - GLuint vertex_shader_id = vertex_shader ? glCreateShader(GL_VERTEX_SHADER) : 0; - GLuint geometry_shader_id = geometry_shader ? glCreateShader(GL_GEOMETRY_SHADER) : 0; - GLuint fragment_shader_id = fragment_shader ? glCreateShader(GL_FRAGMENT_SHADER) : 0; +GLuint LoadShader(const char* source, GLenum type) { + const char* debug_type; + switch (type) { + case GL_VERTEX_SHADER: + debug_type = "vertex"; + break; + case GL_GEOMETRY_SHADER: + debug_type = "geometry"; + break; + case GL_FRAGMENT_SHADER: + debug_type = "fragment"; + break; + default: + UNREACHABLE(); + } + GLuint shader_id = glCreateShader(type); + glShaderSource(shader_id, 1, &source, nullptr); + NGLOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); + glCompileShader(shader_id); GLint result = GL_FALSE; - int info_log_length; - - if (vertex_shader) { - // Compile Vertex Shader - LOG_DEBUG(Render_OpenGL, "Compiling vertex shader..."); - - glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr); - glCompileShader(vertex_shader_id); - - // Check Vertex Shader - glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result); - glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length); - - if (info_log_length > 1) { - std::vector<char> vertex_shader_error(info_log_length); - glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]); - if (result == GL_TRUE) { - LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]); - } else { - LOG_CRITICAL(Render_OpenGL, "Error compiling vertex shader:\n%s", - &vertex_shader_error[0]); - } - } - } - - if (geometry_shader) { - // Compile Geometry Shader - LOG_DEBUG(Render_OpenGL, "Compiling geometry shader..."); - - glShaderSource(geometry_shader_id, 1, &geometry_shader, nullptr); - glCompileShader(geometry_shader_id); - - // Check Geometry Shader - glGetShaderiv(geometry_shader_id, GL_COMPILE_STATUS, &result); - glGetShaderiv(geometry_shader_id, GL_INFO_LOG_LENGTH, &info_log_length); - - if (info_log_length > 1) { - std::vector<char> geometry_shader_error(info_log_length); - glGetShaderInfoLog(geometry_shader_id, info_log_length, nullptr, - &geometry_shader_error[0]); - if (result == GL_TRUE) { - LOG_DEBUG(Render_OpenGL, "%s", &geometry_shader_error[0]); - } else { - LOG_CRITICAL(Render_OpenGL, "Error compiling geometry shader:\n%s", - &geometry_shader_error[0]); - } - } - } - - if (fragment_shader) { - // Compile Fragment Shader - LOG_DEBUG(Render_OpenGL, "Compiling fragment shader..."); - - glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr); - glCompileShader(fragment_shader_id); - - // Check Fragment Shader - glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result); - glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length); - - if (info_log_length > 1) { - std::vector<char> fragment_shader_error(info_log_length); - glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, - &fragment_shader_error[0]); - if (result == GL_TRUE) { - LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]); - } else { - LOG_CRITICAL(Render_OpenGL, "Error compiling fragment shader:\n%s", - &fragment_shader_error[0]); - } - } - } - - // Link the program - LOG_DEBUG(Render_OpenGL, "Linking program..."); - - GLuint program_id = glCreateProgram(); - if (vertex_shader) { - glAttachShader(program_id, vertex_shader_id); - } - if (geometry_shader) { - glAttachShader(program_id, geometry_shader_id); - } - if (fragment_shader) { - glAttachShader(program_id, fragment_shader_id); - } - - if (!feedback_vars.empty()) { - auto varyings = feedback_vars; - glTransformFeedbackVaryings(program_id, static_cast<GLsizei>(feedback_vars.size()), - &varyings[0], GL_INTERLEAVED_ATTRIBS); - } - - if (separable_program) { - glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE); - } - - glLinkProgram(program_id); - - // Check the program - glGetProgramiv(program_id, GL_LINK_STATUS, &result); - glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length); + GLint info_log_length; + glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result); + glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &info_log_length); if (info_log_length > 1) { - std::vector<char> program_error(info_log_length); - glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); + std::string shader_error(info_log_length, ' '); + glGetShaderInfoLog(shader_id, info_log_length, nullptr, &shader_error[0]); if (result == GL_TRUE) { - LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]); + NGLOG_DEBUG(Render_OpenGL, "{}", shader_error); } else { - LOG_CRITICAL(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]); + NGLOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error); } } - - // If the program linking failed at least one of the shaders was probably bad - if (result == GL_FALSE) { - if (vertex_shader) { - LOG_CRITICAL(Render_OpenGL, "Vertex shader:\n%s", vertex_shader); - } - if (geometry_shader) { - LOG_CRITICAL(Render_OpenGL, "Geometry shader:\n%s", geometry_shader); - } - if (fragment_shader) { - LOG_CRITICAL(Render_OpenGL, "Fragment shader:\n%s", fragment_shader); - } - } - ASSERT_MSG(result == GL_TRUE, "Shader not linked"); - - if (vertex_shader) { - glDetachShader(program_id, vertex_shader_id); - glDeleteShader(vertex_shader_id); - } - if (geometry_shader) { - glDetachShader(program_id, geometry_shader_id); - glDeleteShader(geometry_shader_id); - } - if (fragment_shader) { - glDetachShader(program_id, fragment_shader_id); - glDeleteShader(fragment_shader_id); - } - - return program_id; + return shader_id; } } // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index fc7b5e080..a1fa9e814 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -6,18 +6,60 @@ #include <vector> #include <glad/glad.h> +#include "common/assert.h" +#include "common/logging/log.h" namespace GLShader { /** + * Utility function to create and compile an OpenGL GLSL shader + * @param source String of the GLSL shader program + * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER) + */ +GLuint LoadShader(const char* source, GLenum type); + +/** * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) - * @param vertex_shader String of the GLSL vertex shader program - * @param geometry_shader String of the GLSL geometry shader program - * @param fragment_shader String of the GLSL fragment shader program - * @returns Handle of the newly created OpenGL shader object + * @param separable_program whether to create a separable program + * @param shaders ID of shaders to attach to the program + * @returns Handle of the newly created OpenGL program object */ -GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader, - const char* fragment_shader, const std::vector<const char*>& feedback_vars = {}, - bool separable_program = false); +template <typename... T> +GLuint LoadProgram(bool separable_program, T... shaders) { + // Link the program + NGLOG_DEBUG(Render_OpenGL, "Linking program..."); + + GLuint program_id = glCreateProgram(); + + ((shaders == 0 ? (void)0 : glAttachShader(program_id, shaders)), ...); + + if (separable_program) { + glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE); + } + + glLinkProgram(program_id); + + // Check the program + GLint result = GL_FALSE; + GLint info_log_length; + glGetProgramiv(program_id, GL_LINK_STATUS, &result); + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length); + + if (info_log_length > 1) { + std::string program_error(info_log_length, ' '); + glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); + if (result == GL_TRUE) { + NGLOG_DEBUG(Render_OpenGL, "{}", program_error); + } else { + NGLOG_ERROR(Render_OpenGL, "Error linking shader:\n{}", program_error); + } + } + + ASSERT_MSG(result == GL_TRUE, "Shader not linked"); + + ((shaders == 0 ? (void)0 : glDetachShader(program_id, shaders)), ...); + + return program_id; +} } // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 6da3a7781..f91dfe36a 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -2,8 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <iterator> #include <glad/glad.h> -#include "common/common_funcs.h" #include "common/logging/log.h" #include "video_core/renderer_opengl/gl_state.h" @@ -192,7 +192,7 @@ void OpenGLState::Apply() const { } // Textures - for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { + for (size_t i = 0; i < std::size(texture_units); ++i) { if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { glActiveTexture(TextureUnits::MaxwellTexture(i).Enum()); glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); @@ -202,6 +202,20 @@ void OpenGLState::Apply() const { } } + // Constbuffers + for (u32 stage = 0; stage < draw.const_buffers.size(); ++stage) { + for (u32 buffer_id = 0; buffer_id < draw.const_buffers[stage].size(); ++buffer_id) { + auto& current = cur_state.draw.const_buffers[stage][buffer_id]; + auto& new_state = draw.const_buffers[stage][buffer_id]; + if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint || + current.ssbo != new_state.ssbo) { + if (new_state.enabled) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, new_state.bindpoint, new_state.ssbo); + } + } + } + } + // Lighting LUTs if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) { glActiveTexture(TextureUnits::LightingLUT.Enum()); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index b18af14bb..75c08e645 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -123,6 +123,12 @@ public: GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING GLuint shader_program; // GL_CURRENT_PROGRAM GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING + struct ConstBufferConfig { + bool enabled = false; + GLuint bindpoint; + GLuint ssbo; + }; + std::array<std::array<ConstBufferConfig, 16>, 5> const_buffers{}; } draw; struct { diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index 4bc2f52e0..e78dc5784 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <memory> #include <glad/glad.h> #include "common/common_types.h" diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 48ee80125..a49265b38 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -10,6 +10,14 @@ #include "common/logging/log.h" #include "video_core/engines/maxwell_3d.h" +using GLvec2 = std::array<GLfloat, 2>; +using GLvec3 = std::array<GLfloat, 3>; +using GLvec4 = std::array<GLfloat, 4>; + +using GLuvec2 = std::array<GLuint, 2>; +using GLuvec3 = std::array<GLuint, 3>; +using GLuvec4 = std::array<GLuint, 4>; + namespace MaxwellToGL { using Maxwell = Tegra::Engines::Maxwell3D::Regs; @@ -23,7 +31,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { return GL_UNSIGNED_BYTE; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size=%s", attrib.SizeString().c_str()); + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); UNREACHABLE(); return {}; } @@ -32,17 +40,33 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { return GL_FLOAT; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type=%s", attrib.TypeString().c_str()); + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); + UNREACHABLE(); + return {}; +} + +inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { + switch (index_format) { + case Maxwell::IndexFormat::UnsignedByte: + return GL_UNSIGNED_BYTE; + case Maxwell::IndexFormat::UnsignedShort: + return GL_UNSIGNED_SHORT; + case Maxwell::IndexFormat::UnsignedInt: + return GL_UNSIGNED_INT; + } + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format)); UNREACHABLE(); return {}; } inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { switch (topology) { + case Maxwell::PrimitiveTopology::Triangles: + return GL_TRIANGLES; case Maxwell::PrimitiveTopology::TriangleStrip: return GL_TRIANGLE_STRIP; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented primitive topology=%d", topology); + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); UNREACHABLE(); return {}; } @@ -54,18 +78,90 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { case Tegra::Texture::TextureFilter::Nearest: return GL_NEAREST; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode=%u", - static_cast<u32>(filter_mode)); + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", + static_cast<u32>(filter_mode)); UNREACHABLE(); return {}; } inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { switch (wrap_mode) { + case Tegra::Texture::WrapMode::Wrap: + return GL_REPEAT; case Tegra::Texture::WrapMode::ClampToEdge: return GL_CLAMP_TO_EDGE; + case Tegra::Texture::WrapMode::ClampOGL: + // TODO(Subv): GL_CLAMP was removed as of OpenGL 3.1, to implement GL_CLAMP, we can use + // GL_CLAMP_TO_BORDER to get the border color of the texture, and then sample the edge to + // manually mix them. However the shader part of this is not yet implemented. + return GL_CLAMP_TO_BORDER; + } + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", + static_cast<u32>(wrap_mode)); + UNREACHABLE(); + return {}; +} + +inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { + switch (equation) { + case Maxwell::Blend::Equation::Add: + return GL_FUNC_ADD; + case Maxwell::Blend::Equation::Subtract: + return GL_FUNC_SUBTRACT; + case Maxwell::Blend::Equation::ReverseSubtract: + return GL_FUNC_REVERSE_SUBTRACT; + case Maxwell::Blend::Equation::Min: + return GL_MIN; + case Maxwell::Blend::Equation::Max: + return GL_MAX; + } + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); + UNREACHABLE(); + return {}; +} + +inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { + switch (factor) { + case Maxwell::Blend::Factor::Zero: + return GL_ZERO; + case Maxwell::Blend::Factor::One: + return GL_ONE; + case Maxwell::Blend::Factor::SourceColor: + return GL_SRC_COLOR; + case Maxwell::Blend::Factor::OneMinusSourceColor: + return GL_ONE_MINUS_SRC_COLOR; + case Maxwell::Blend::Factor::SourceAlpha: + return GL_SRC_ALPHA; + case Maxwell::Blend::Factor::OneMinusSourceAlpha: + return GL_ONE_MINUS_SRC_ALPHA; + case Maxwell::Blend::Factor::DestAlpha: + return GL_DST_ALPHA; + case Maxwell::Blend::Factor::OneMinusDestAlpha: + return GL_ONE_MINUS_DST_ALPHA; + case Maxwell::Blend::Factor::DestColor: + return GL_DST_COLOR; + case Maxwell::Blend::Factor::OneMinusDestColor: + return GL_ONE_MINUS_DST_COLOR; + case Maxwell::Blend::Factor::SourceAlphaSaturate: + return GL_SRC_ALPHA_SATURATE; + case Maxwell::Blend::Factor::Source1Color: + return GL_SRC1_COLOR; + case Maxwell::Blend::Factor::OneMinusSource1Color: + return GL_ONE_MINUS_SRC1_COLOR; + case Maxwell::Blend::Factor::Source1Alpha: + return GL_SRC1_ALPHA; + case Maxwell::Blend::Factor::OneMinusSource1Alpha: + return GL_ONE_MINUS_SRC1_ALPHA; + case Maxwell::Blend::Factor::ConstantColor: + return GL_CONSTANT_COLOR; + case Maxwell::Blend::Factor::OneMinusConstantColor: + return GL_ONE_MINUS_CONSTANT_COLOR; + case Maxwell::Blend::Factor::ConstantAlpha: + return GL_CONSTANT_ALPHA; + case Maxwell::Blend::Factor::OneMinusConstantAlpha: + return GL_ONE_MINUS_CONSTANT_ALPHA; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode=%u", static_cast<u32>(wrap_mode)); + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); UNREACHABLE(); return {}; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 78b50b227..ab0acb20a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -9,13 +9,10 @@ #include <memory> #include <glad/glad.h> #include "common/assert.h" -#include "common/bit_field.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" -#include "core/hw/hw.h" -#include "core/hw/lcd.h" #include "core/memory.h" #include "core/settings.h" #include "core/tracer/recorder.h" @@ -57,7 +54,7 @@ uniform sampler2D color_texture; void main() { // Swap RGBA -> ABGR so we don't have to do this on the CPU. This needs to change if we have to // support more framebuffer pixel formats. - color = texture(color_texture, frag_tex_coord).abgr; + color = texture(color_texture, frag_tex_coord); } )"; @@ -210,7 +207,7 @@ void RendererOpenGL::InitOpenGLObjects() { 0.0f); // Link shaders and get variable locations - shader.Create(vertex_shader, nullptr, fragment_shader); + shader.CreateFromSource(vertex_shader, nullptr, fragment_shader); state.draw.shader_program = shader.handle; state.Apply(); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); @@ -311,10 +308,10 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, } std::array<ScreenRectVertex, 4> vertices = {{ - ScreenRectVertex(x, y, texcoords.top, right), - ScreenRectVertex(x + w, y, texcoords.bottom, right), - ScreenRectVertex(x, y + h, texcoords.top, left), - ScreenRectVertex(x + w, y + h, texcoords.bottom, left), + ScreenRectVertex(x, y, texcoords.top, left), + ScreenRectVertex(x + w, y, texcoords.bottom, left), + ScreenRectVertex(x, y + h, texcoords.top, right), + ScreenRectVertex(x + w, y + h, texcoords.bottom, right), }}; state.texture_units[0].texture_2d = screen_info.display_texture; diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c52f40037..2cc6d9a00 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -72,7 +72,7 @@ private: // OpenGL object IDs OGLVertexArray vertex_array; OGLBuffer vertex_buffer; - OGLShader shader; + OGLProgram shader; /// Display information for Switch screen ScreenInfo screen_info; diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 2e87281eb..4df687786 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -48,31 +48,39 @@ u32 BytesPerPixel(TextureFormat format) { case TextureFormat::DXT1: // In this case a 'pixel' actually refers to a 4x4 tile. return 8; + case TextureFormat::DXT23: + case TextureFormat::DXT45: + // In this case a 'pixel' actually refers to a 4x4 tile. + return 16; case TextureFormat::A8R8G8B8: return 4; + case TextureFormat::B5G6R5: + return 2; default: UNIMPLEMENTED_MSG("Format not implemented"); break; } } -std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height) { +std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, + u32 block_height) { u8* data = Memory::GetPointer(address); u32 bytes_per_pixel = BytesPerPixel(format); - static constexpr u32 DefaultBlockHeight = 16; - std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); switch (format) { case TextureFormat::DXT1: - // In the DXT1 format, each 4x4 tile is swizzled instead of just individual pixel values. + case TextureFormat::DXT23: + case TextureFormat::DXT45: + // In the DXT formats, each 4x4 tile is swizzled instead of just individual pixel values. CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, - unswizzled_data.data(), true, DefaultBlockHeight); + unswizzled_data.data(), true, block_height); break; case TextureFormat::A8R8G8B8: + case TextureFormat::B5G6R5: CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, - unswizzled_data.data(), true, DefaultBlockHeight); + unswizzled_data.data(), true, block_height); break; default: UNIMPLEMENTED_MSG("Format not implemented"); @@ -89,7 +97,10 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat // TODO(Subv): Implement. switch (format) { case TextureFormat::DXT1: + case TextureFormat::DXT23: + case TextureFormat::DXT45: case TextureFormat::A8R8G8B8: + case TextureFormat::B5G6R5: // TODO(Subv): For the time being just forward the same data without any decoding. rgba_data = texture_data; break; diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 0c21694ff..a700911cf 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -14,7 +14,8 @@ namespace Texture { /** * Unswizzles a swizzled texture without changing its format. */ -std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height); +std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, + u32 block_height = TICEntry::DefaultBlockHeight); /** * Decodes an unswizzled texture into a A8R8G8B8 texture. diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index c12ed6e1d..86e45aa88 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -4,6 +4,7 @@ #pragma once +#include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -13,8 +14,11 @@ namespace Tegra { namespace Texture { enum class TextureFormat : u32 { - A8R8G8B8 = 8, + A8R8G8B8 = 0x8, + B5G6R5 = 0x15, DXT1 = 0x24, + DXT23 = 0x25, + DXT45 = 0x26, }; enum class TextureType : u32 { @@ -55,6 +59,8 @@ union TextureHandle { static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size"); struct TICEntry { + static constexpr u32 DefaultBlockHeight = 16; + union { u32 raw; BitField<0, 7, TextureFormat> format; @@ -68,7 +74,12 @@ struct TICEntry { BitField<0, 16, u32> address_high; BitField<21, 3, TICHeaderVersion> header_version; }; - INSERT_PADDING_BYTES(4); + union { + BitField<3, 3, u32> block_height; + + // High 16 bits of the pitch value + BitField<0, 16, u32> pitch_high; + }; union { BitField<0, 16, u32> width_minus_1; BitField<23, 4, TextureType> texture_type; @@ -80,6 +91,13 @@ struct TICEntry { return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); } + u32 Pitch() const { + ASSERT(header_version == TICHeaderVersion::Pitch || + header_version == TICHeaderVersion::PitchColorKey); + // The pitch value is 21 bits, and is 32B aligned. + return pitch_high << 5; + } + u32 Width() const { return width_minus_1 + 1; } @@ -88,6 +106,13 @@ struct TICEntry { return height_minus_1 + 1; } + u32 BlockHeight() const { + ASSERT(header_version == TICHeaderVersion::BlockLinear || + header_version == TICHeaderVersion::BlockLinearColorKey); + // The block height is stored in log2 format. + return 1 << block_height; + } + bool IsTiled() const { return header_version == TICHeaderVersion::BlockLinear || header_version == TICHeaderVersion::BlockLinearColorKey; diff --git a/src/video_core/utils.h b/src/video_core/utils.h index be0f7e22b..e0a14d48f 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h @@ -151,7 +151,7 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe const u32 coarse_y = y & ~127; u32 morton_offset = GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel; - u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel; + u32 gl_pixel_index = (x + y * width) * gl_bytes_per_pixel; data_ptrs[morton_to_gl] = morton_data + morton_offset; data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index]; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 34e33170e..20796e92c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -44,6 +44,15 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #endif +#ifdef _WIN32 +extern "C" { +// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable +// graphics +__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +} +#endif + /** * "Callouts" are one-time instructional messages shown to the user. In the config settings, there * is a bitfield "callout_flags" options, used to track if a message has already been shown to the @@ -776,9 +785,11 @@ void GMainWindow::closeEvent(QCloseEvent* event) { return; } - UISettings::values.geometry = saveGeometry(); + if (ui.action_Fullscreen->isChecked()) { + UISettings::values.geometry = saveGeometry(); + UISettings::values.renderwindow_geometry = render_window->saveGeometry(); + } UISettings::values.state = saveState(); - UISettings::values.renderwindow_geometry = render_window->saveGeometry(); #if MICROPROFILE_ENABLED UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry(); UISettings::values.microprofile_visible = microProfileDialog->isVisible(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 0a4644500..39603e881 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -37,6 +37,15 @@ #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#ifdef _WIN32 +extern "C" { +// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable +// graphics +__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +} +#endif + static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 << " [options] <filename>\n" |