-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update BPMemory.h to Dolphin's version
Note that there are some small differences: - TextureFormat, TLUTFormat, and EFBCopyFormat are normally in a different header, but I've put them here instead - I've added a function to set the copy format to UPE_Copy - I've removed the use of Common/Inline.h - I've removed non-implemented functions and the actual BP memory struct - I've made the enum of various registers have a base type of u32 (which allows bitshifting with it without warnings)
- Loading branch information
Showing
12 changed files
with
2,489 additions
and
859 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright 2021 Dolphin Emulator Project | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
#pragma once | ||
|
||
#include "Common/EnumMap.h" | ||
|
||
#include <fmt/format.h> | ||
#include <type_traits> | ||
|
||
/* | ||
* Helper for using enums with fmt. | ||
* | ||
* Usage example: | ||
* | ||
* enum class Foo | ||
* { | ||
* A = 0, | ||
* B = 1, | ||
* C = 2, | ||
* }; | ||
* | ||
* template <> | ||
* struct fmt::formatter<Foo> : EnumFormatter<Foo::C> | ||
* { | ||
* constexpr formatter() : EnumFormatter({"A", "B", "C"}) {} | ||
* }; | ||
* | ||
* enum class Bar | ||
* { | ||
* D = 0, | ||
* E = 1, | ||
* F = 3, | ||
* }; | ||
* | ||
* template <> | ||
* struct fmt::formatter<Bar> : EnumFormatter<Bar::F> | ||
* { | ||
* // using std::array here fails due to nullptr not being const char*, at least in MSVC | ||
* // (but only when a field is used; directly in the constructor is OK) | ||
* static constexpr array_type names = {"D", "E", nullptr, "F"}; | ||
* constexpr formatter() : EnumFormatter(names) {} | ||
* }; | ||
*/ | ||
template <auto last_member, typename = decltype(last_member)> | ||
class EnumFormatter | ||
{ | ||
// The second template argument is needed to avoid compile errors from ambiguity with multiple | ||
// enums with the same number of members in GCC prior to 8. See https://godbolt.org/z/xcKaW1seW | ||
// and https://godbolt.org/z/hz7Yqq1P5 | ||
using T = decltype(last_member); | ||
static_assert(std::is_enum_v<T>); | ||
|
||
public: | ||
constexpr auto parse(fmt::format_parse_context& ctx) | ||
{ | ||
auto it = ctx.begin(), end = ctx.end(); | ||
// 'u' for user display, 's' for shader generation, 'n' for name only | ||
if (it != end && (*it == 'u' || *it == 's' || *it == 'n')) | ||
format_type = *it++; | ||
return it; | ||
} | ||
|
||
template <typename FormatContext> | ||
auto format(const T& e, FormatContext& ctx) const | ||
{ | ||
const auto value_s = static_cast<std::underlying_type_t<T>>(e); // Possibly signed | ||
const auto value_u = static_cast<std::make_unsigned_t<T>>(value_s); // Always unsigned | ||
const bool has_name = m_names.InBounds(e) && m_names[e] != nullptr; | ||
|
||
switch (format_type) | ||
{ | ||
default: | ||
case 'u': | ||
if (has_name) | ||
return fmt::format_to(ctx.out(), "{} ({})", m_names[e], value_s); | ||
else | ||
return fmt::format_to(ctx.out(), "Invalid ({})", value_s); | ||
case 's': | ||
if (has_name) | ||
return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value_u, m_names[e]); | ||
else | ||
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */", value_u); | ||
case 'n': | ||
if (has_name) | ||
return fmt::format_to(ctx.out(), "{}", m_names[e]); | ||
else | ||
return fmt::format_to(ctx.out(), "Invalid ({})", value_s); | ||
} | ||
} | ||
|
||
protected: | ||
// This is needed because std::array deduces incorrectly if nullptr is included in the list | ||
using array_type = Common::EnumMap<const char*, last_member>; | ||
|
||
constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {} | ||
|
||
const array_type m_names; | ||
char format_type = 'u'; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright 2021 Dolphin Emulator Project | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
#pragma once | ||
|
||
#include <array> | ||
#include <type_traits> | ||
|
||
#include "Common/TypeUtils.h" | ||
|
||
template <std::size_t position, std::size_t bits, typename T, typename StorageType> | ||
struct BitField; | ||
|
||
namespace Common | ||
{ | ||
// A type that allows lookup of values associated with an enum as the key. | ||
// Designed for enums whose numeric values start at 0 and increment continuously with few gaps. | ||
template <typename V, auto last_member, typename = decltype(last_member)> | ||
class EnumMap final | ||
{ | ||
// The third template argument is needed to avoid compile errors from ambiguity with multiple | ||
// enums with the same number of members in GCC prior to 8. See https://godbolt.org/z/xcKaW1seW | ||
// and https://godbolt.org/z/hz7Yqq1P5 | ||
using T = decltype(last_member); | ||
static_assert(std::is_enum_v<T>); | ||
static constexpr size_t s_size = static_cast<size_t>(last_member) + 1; | ||
|
||
using array_type = std::array<V, s_size>; | ||
using iterator = typename array_type::iterator; | ||
using const_iterator = typename array_type::const_iterator; | ||
|
||
public: | ||
constexpr EnumMap() = default; | ||
constexpr EnumMap(const EnumMap& other) = default; | ||
constexpr EnumMap& operator=(const EnumMap& other) = default; | ||
constexpr EnumMap(EnumMap&& other) = default; | ||
constexpr EnumMap& operator=(EnumMap&& other) = default; | ||
|
||
// Constructor that accepts exactly size Vs (enforcing that all must be specified). | ||
template <typename... T, typename = std::enable_if_t<Common::IsNOf<V, s_size, T...>::value>> | ||
constexpr EnumMap(T... values) : m_array{static_cast<V>(values)...} | ||
{ | ||
} | ||
|
||
constexpr const V& operator[](T key) const { return m_array[static_cast<std::size_t>(key)]; } | ||
constexpr V& operator[](T key) { return m_array[static_cast<std::size_t>(key)]; } | ||
|
||
// These only exist to perform the safety check; without them, BitField's implicit conversion | ||
// would work (but since BitField is used for game-generated data, we need to be careful about | ||
// bounds-checking) | ||
template <std::size_t position, std::size_t bits, typename StorageType> | ||
constexpr const V& operator[](BitField<position, bits, T, StorageType> key) const | ||
{ | ||
static_assert(1 << bits == s_size, "Unsafe indexing into EnumMap (may go out of bounds)"); | ||
return m_array[static_cast<std::size_t>(key.Value())]; | ||
} | ||
template <std::size_t position, std::size_t bits, typename StorageType> | ||
constexpr V& operator[](BitField<position, bits, T, StorageType> key) | ||
{ | ||
static_assert(1 << bits == s_size, "Unsafe indexing into EnumMap (may go out of bounds)"); | ||
return m_array[static_cast<std::size_t>(key.value())]; | ||
} | ||
|
||
constexpr bool InBounds(T key) const { return static_cast<std::size_t>(key) < s_size; } | ||
|
||
constexpr size_t size() const noexcept { return s_size; } | ||
|
||
constexpr V* data() { return m_array.data(); } | ||
constexpr const V* data() const { return m_array.data(); } | ||
|
||
constexpr iterator begin() { return m_array.begin(); } | ||
constexpr iterator end() { return m_array.end(); } | ||
constexpr const_iterator begin() const { return m_array.begin(); } | ||
constexpr const_iterator end() const { return m_array.end(); } | ||
constexpr const_iterator cbegin() const { return m_array.cbegin(); } | ||
constexpr const_iterator cend() const { return m_array.cend(); } | ||
|
||
constexpr void fill(const V& v) { m_array.fill(v); } | ||
|
||
private: | ||
array_type m_array{}; | ||
}; | ||
} // namespace Common |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Copyright 2021 Dolphin Emulator Project | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <type_traits> | ||
|
||
namespace Common | ||
{ | ||
template <typename> | ||
struct MemberPointerInfo; | ||
// Helper to get information about a pointer to a data member. | ||
// See https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_members | ||
// This template takes the type for a member pointer. | ||
template <typename M, typename O> | ||
struct MemberPointerInfo<M O::*> | ||
{ | ||
using MemberType = M; | ||
using ObjectType = O; | ||
}; | ||
|
||
// This template takes a specific member pointer. | ||
template <auto member_pointer> | ||
using MemberType = typename MemberPointerInfo<decltype(member_pointer)>::MemberType; | ||
|
||
// This template takes a specific member pointer. | ||
template <auto member_pointer> | ||
using ObjectType = typename MemberPointerInfo<decltype(member_pointer)>::ObjectType; | ||
|
||
namespace detail | ||
{ | ||
template <int x> | ||
struct Data | ||
{ | ||
static constexpr int GetX() { return x; } | ||
}; | ||
struct Foo | ||
{ | ||
Data<1> a; | ||
Data<2> b; | ||
int c; | ||
}; | ||
struct Bar : Foo | ||
{ | ||
int d; | ||
}; | ||
|
||
static_assert(std::is_same_v<MemberType<&Foo::a>, Data<1>>); | ||
static_assert(MemberType<&Foo::a>::GetX() == 1); | ||
static_assert(std::is_same_v<MemberType<&Foo::b>, Data<2>>); | ||
static_assert(MemberType<&Foo::b>::GetX() == 2); | ||
static_assert(std::is_same_v<MemberType<&Foo::c>, int>); | ||
|
||
static_assert(std::is_same_v<ObjectType<&Foo::a>, Foo>); | ||
static_assert(std::is_same_v<ObjectType<&Foo::b>, Foo>); | ||
static_assert(std::is_same_v<ObjectType<&Foo::c>, Foo>); | ||
|
||
static_assert(std::is_same_v<MemberType<&Foo::c>, MemberPointerInfo<int Foo::*>::MemberType>); | ||
static_assert(std::is_same_v<ObjectType<&Foo::c>, MemberPointerInfo<int Foo::*>::ObjectType>); | ||
|
||
static_assert(std::is_same_v<MemberType<&Bar::c>, int>); | ||
static_assert(std::is_same_v<MemberType<&Bar::d>, int>); | ||
|
||
static_assert(std::is_same_v<ObjectType<&Bar::d>, Bar>); | ||
// Somewhat unexpected behavior: | ||
static_assert(std::is_same_v<ObjectType<&Bar::c>, Foo>); | ||
static_assert(!std::is_same_v<ObjectType<&Bar::c>, Bar>); | ||
} // namespace detail | ||
|
||
// Template for checking if Types is count occurrences of T. | ||
template <typename T, size_t count, typename... Ts> | ||
struct IsNOf : std::integral_constant<bool, std::conjunction_v<std::is_convertible<Ts, T>...> && | ||
sizeof...(Ts) == count> | ||
{ | ||
}; | ||
|
||
static_assert(IsNOf<int, 0>::value); | ||
static_assert(!IsNOf<int, 0, int>::value); | ||
static_assert(IsNOf<int, 1, int>::value); | ||
static_assert(!IsNOf<int, 1>::value); | ||
static_assert(!IsNOf<int, 1, int, int>::value); | ||
static_assert(IsNOf<int, 2, int, int>::value); | ||
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed | ||
static_assert(!IsNOf<int, 2, int, char*>::value); | ||
} // namespace Common |
Oops, something went wrong.