diff --git a/README.md b/README.md index d79cb0e13..e63d925f9 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2177. +This is the source code for early-access 2179. ## Legal Notice diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7bbf5fa74..919da4a53 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -55,6 +55,7 @@ add_library(common STATIC dynamic_library.h error.cpp error.h + expected.h fiber.cpp fiber.h fs/file.cpp diff --git a/src/common/expected.h b/src/common/expected.h new file mode 100755 index 000000000..c8d8579c1 --- /dev/null +++ b/src/common/expected.h @@ -0,0 +1,987 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This is based on the proposed implementation of std::expected (P0323) +// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp + +#pragma once + +#include +#include + +namespace Common { + +template +class Expected; + +template +class Unexpected { +public: + Unexpected() = delete; + + constexpr explicit Unexpected(const E& e) : m_val{e} {} + + constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {} + + constexpr E& value() & { + return m_val; + } + + constexpr const E& value() const& { + return m_val; + } + + constexpr E&& value() && { + return std::move(m_val); + } + + constexpr const E&& value() const&& { + return std::move(m_val); + } + +private: + E m_val; +}; + +template +constexpr auto operator<=>(const Unexpected& lhs, const Unexpected& rhs) { + return lhs.value() <=> rhs.value(); +} + +struct unexpect_t { + constexpr explicit unexpect_t() = default; +}; + +namespace detail { + +struct no_init_t { + constexpr explicit no_init_t() = default; +}; + +/** + * This specialization is for when T is not trivially destructible, + * so the destructor must be called on destruction of `expected' + * Additionally, this requires E to be trivially destructible + */ +template > +requires std::is_trivially_destructible_v +struct expected_storage_base { + constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} + + constexpr expected_storage_base(no_init_t) : m_has_val{false} {} + + template >* = nullptr> + constexpr expected_storage_base(std::in_place_t, Args&&... args) + : m_val{std::forward(args)...}, m_has_val{true} {} + + template &, Args&&...>>* = + nullptr> + constexpr expected_storage_base(std::in_place_t, std::initializer_list il, Args&&... args) + : m_val{il, std::forward(args)...}, m_has_val{true} {} + + template >* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + : m_unexpect{std::forward(args)...}, m_has_val{false} {} + + template &, Args&&...>>* = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, + Args&&... args) + : m_unexpect{il, std::forward(args)...}, m_has_val{false} {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + + union { + T m_val; + Unexpected m_unexpect; + }; + + bool m_has_val; +}; + +/** + * This specialization is for when T is trivially destructible, + * so the destructor of `expected` can be trivial + * Additionally, this requires E to be trivially destructible + */ +template +requires std::is_trivially_destructible_v +struct expected_storage_base { + constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} + + constexpr expected_storage_base(no_init_t) : m_has_val{false} {} + + template >* = nullptr> + constexpr expected_storage_base(std::in_place_t, Args&&... args) + : m_val{std::forward(args)...}, m_has_val{true} {} + + template &, Args&&...>>* = + nullptr> + constexpr expected_storage_base(std::in_place_t, std::initializer_list il, Args&&... args) + : m_val{il, std::forward(args)...}, m_has_val{true} {} + + template >* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) + : m_unexpect{std::forward(args)...}, m_has_val{false} {} + + template &, Args&&...>>* = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, + Args&&... args) + : m_unexpect{il, std::forward(args)...}, m_has_val{false} {} + + ~expected_storage_base() = default; + + union { + T m_val; + Unexpected m_unexpect; + }; + + bool m_has_val; +}; + +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template + void construct(Args&&... args) noexcept { + new (std::addressof(this->m_val)) T{std::forward(args)...}; + this->m_has_val = true; + } + + template + void construct_with(Rhs&& rhs) noexcept { + new (std::addressof(this->m_val)) T{std::forward(rhs).get()}; + this->m_has_val = true; + } + + template + void construct_error(Args&&... args) noexcept { + new (std::addressof(this->m_unexpect)) Unexpected{std::forward(args)...}; + this->m_has_val = false; + } + + void assign(const expected_operations_base& rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~Unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base&& rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~Unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(rhs); + } + } + + template + void assign_common(Rhs&& rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { + return this->m_has_val; + } + + constexpr T& get() & { + return this->m_val; + } + + constexpr const T& get() const& { + return this->m_val; + } + + constexpr T&& get() && { + return std::move(this->m_val); + } + + constexpr const T&& get() const&& { + return std::move(this->m_val); + } + + constexpr Unexpected& geterr() & { + return this->m_unexpect; + } + + constexpr const Unexpected& geterr() const& { + return this->m_unexpect; + } + + constexpr Unexpected&& geterr() && { + return std::move(this->m_unexpect); + } + + constexpr const Unexpected&& geterr() const&& { + return std::move(this->m_unexpect); + } + + constexpr void destroy_val() { + get().~T(); + } +}; + +/** + * This manages conditionally having a trivial copy constructor + * This specialization is for when T is trivially copy constructible + * Additionally, this requires E to be trivially copy constructible + */ +template > +requires std::is_trivially_copy_constructible_v +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +/** + * This specialization is for when T is not trivially copy constructible + * Additionally, this requires E to be trivially copy constructible + */ +template +requires std::is_trivially_copy_constructible_v +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + + expected_copy_base(const expected_copy_base& rhs) + : expected_operations_base{no_init_t{}} { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base&&) = default; + + expected_copy_base& operator=(const expected_copy_base&) = default; + + expected_copy_base& operator=(expected_copy_base&&) = default; +}; + +/** + * This manages conditionally having a trivial move constructor + * This specialization is for when T is trivially move constructible + * Additionally, this requires E to be trivially move constructible + */ +template > +requires std::is_trivially_move_constructible_v +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; + +/** + * This specialization is for when T is not trivially move constructible + * Additionally, this requires E to be trivially move constructible + */ +template +requires std::is_trivially_move_constructible_v +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + + expected_move_base(const expected_move_base&) = default; + + expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v) + : expected_copy_base{no_init_t{}} { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + + expected_move_base& operator=(const expected_move_base&) = default; + + expected_move_base& operator=(expected_move_base&&) = default; +}; + +/** + * This manages conditionally having a trivial copy assignment operator + * This specialization is for when T is trivially copy assignable + * Additionally, this requires E to be trivially copy assignable + */ +template , + std::is_trivially_copy_constructible, + std::is_trivially_destructible>> +requires std::conjunction_v, + std::is_trivially_copy_constructible, + std::is_trivially_destructible> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +/** + * This specialization is for when T is not trivially copy assignable + * Additionally, this requires E to be trivially copy assignable + */ +template +requires std::conjunction_v, + std::is_trivially_copy_constructible, + std::is_trivially_destructible> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + + expected_copy_assign_base(const expected_copy_assign_base&) = default; + + expected_copy_assign_base(expected_copy_assign_base&&) = default; + + expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) { + this->assign(rhs); + return *this; + } + + expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default; +}; + +/** + * This manages conditionally having a trivial move assignment operator + * This specialization is for when T is trivially move assignable + * Additionally, this requires E to be trivially move assignable + */ +template , + std::is_trivially_move_constructible, + std::is_trivially_destructible>> +requires std::conjunction_v, + std::is_trivially_move_constructible, + std::is_trivially_destructible> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; + +/** + * This specialization is for when T is not trivially move assignable + * Additionally, this requires E to be trivially move assignable + */ +template +requires std::conjunction_v, + std::is_trivially_move_constructible, + std::is_trivially_destructible> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + + expected_move_assign_base(const expected_move_assign_base&) = default; + + expected_move_assign_base(expected_move_assign_base&&) = default; + + expected_move_assign_base& operator=(const expected_move_assign_base&) = default; + + expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept( + std::conjunction_v, + std::is_nothrow_move_assignable>) { + this->assign(std::move(rhs)); + return *this; + } +}; + +/** + * expected_delete_ctor_base will conditionally delete copy and move constructors + * depending on whether T is copy/move constructible + * Additionally, this requires E to be copy/move constructible + */ +template , + bool EnableMove = std::is_move_constructible_v> +requires std::conjunction_v, std::is_move_constructible> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base&) = default; + expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; + expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; + expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; +}; + +template +requires std::conjunction_v, std::is_move_constructible> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base&) = default; + expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; + expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; + expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; +}; + +template +requires std::conjunction_v, std::is_move_constructible> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; + expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; + expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; + expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; +}; + +template +requires std::conjunction_v, std::is_move_constructible> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; + expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; + expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; + expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; +}; + +/** + * expected_delete_assign_base will conditionally delete copy and move assignment operators + * depending on whether T is copy/move constructible + assignable + * Additionally, this requires E to be copy/move constructible + assignable + */ +template < + typename T, typename E, + bool EnableCopy = std::conjunction_v, std::is_copy_assignable>, + bool EnableMove = std::conjunction_v, std::is_move_assignable>> +requires std::conjunction_v, std::is_move_constructible, + std::is_copy_assignable, std::is_move_assignable> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base&) = default; + expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; + expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default; + expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default; +}; + +template +requires std::conjunction_v, std::is_move_constructible, + std::is_copy_assignable, std::is_move_assignable> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base&) = default; + expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; + expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default; + expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete; +}; + +template +requires std::conjunction_v, std::is_move_constructible, + std::is_copy_assignable, std::is_move_assignable> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base&) = default; + expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; + expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete; + expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default; +}; + +template +requires std::conjunction_v, std::is_move_constructible, + std::is_copy_assignable, std::is_move_assignable> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base&) = default; + expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; + expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete; + expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete; +}; + +/** + * This is needed to be able to construct the expected_default_ctor_base which follows, + * while still conditionally deleting the default constructor. + */ +struct default_constructor_tag { + constexpr explicit default_constructor_tag() = default; +}; + +/** + * expected_default_ctor_base will ensure that expected + * has a deleted default constructor if T is not default constructible + * This specialization is for when T is default constructible + */ +template > +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default; + expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default; + expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +template +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default; + expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default; + expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +template +using expected_enable_forward_value = + std::enable_if_t && + !std::is_same_v, std::in_place_t> && + !std::is_same_v, std::remove_cvref_t> && + !std::is_same_v, std::remove_cvref_t>>; + +template +using expected_enable_from_other = std::enable_if_t< + std::is_constructible_v && std::is_constructible_v && + !std::is_constructible_v&> && !std::is_constructible_v&&> && + !std::is_constructible_v&> && + !std::is_constructible_v&&> && + !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && + !std::is_convertible_v&, T> && + !std::is_convertible_v&&, T>>; + +} // namespace detail + +template +class Expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { +public: + using value_type = T; + using error_type = E; + using unexpected_type = Unexpected; + + constexpr Expected() = default; + constexpr Expected(const Expected&) = default; + constexpr Expected(Expected&&) = default; + Expected& operator=(const Expected&) = default; + Expected& operator=(Expected&&) = default; + + template >* = nullptr> + constexpr Expected(std::in_place_t, Args&&... args) + : impl_base{std::in_place, std::forward(args)...}, + ctor_base{detail::default_constructor_tag{}} {} + + template &, Args&&...>>* = + nullptr> + constexpr Expected(std::in_place_t, std::initializer_list il, Args&&... args) + : impl_base{std::in_place, il, std::forward(args)...}, + ctor_base{detail::default_constructor_tag{}} {} + + template >* = nullptr, + std::enable_if_t>* = nullptr> + constexpr explicit Expected(const Unexpected& e) + : impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {} + + template >* = nullptr, + std::enable_if_t>* = nullptr> + constexpr Expected(Unexpected const& e) + : impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {} + + template >* = nullptr, + std::enable_if_t>* = nullptr> + constexpr explicit Expected(Unexpected&& e) noexcept(std::is_nothrow_constructible_v) + : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ + detail::default_constructor_tag{}} {} + + template >* = nullptr, + std::enable_if_t>* = nullptr> + constexpr Expected(Unexpected&& e) noexcept(std::is_nothrow_constructible_v) + : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ + detail::default_constructor_tag{}} {} + + template >* = nullptr> + constexpr explicit Expected(unexpect_t, Args&&... args) + : impl_base{unexpect_t{}, std::forward(args)...}, + ctor_base{detail::default_constructor_tag{}} {} + + template &, Args&&...>>* = + nullptr> + constexpr explicit Expected(unexpect_t, std::initializer_list il, Args&&... args) + : impl_base{unexpect_t{}, il, std::forward(args)...}, + ctor_base{detail::default_constructor_tag{}} {} + + template && + std::is_convertible_v)>* = nullptr, + detail::expected_enable_from_other* = nullptr> + constexpr explicit Expected(const Expected& rhs) + : ctor_base{detail::default_constructor_tag{}} { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template && + std::is_convertible_v)>* = nullptr, + detail::expected_enable_from_other* = nullptr> + constexpr Expected(const Expected& rhs) : ctor_base{detail::default_constructor_tag{}} { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template && std::is_convertible_v)>* = + nullptr, + detail::expected_enable_from_other* = nullptr> + constexpr explicit Expected(Expected&& rhs) + : ctor_base{detail::default_constructor_tag{}} { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template && std::is_convertible_v)>* = + nullptr, + detail::expected_enable_from_other* = nullptr> + constexpr Expected(Expected&& rhs) : ctor_base{detail::default_constructor_tag{}} { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template >* = nullptr, + detail::expected_enable_forward_value* = nullptr> + constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward(v)} {} + + template >* = nullptr, + detail::expected_enable_forward_value* = nullptr> + constexpr Expected(U&& v) : Expected{std::in_place, std::forward(v)} {} + + template >* = nullptr, + std::enable_if_t<( + !std::is_same_v, std::remove_cvref_t> && + !std::conjunction_v, std::is_same>> && + std::is_constructible_v && std::is_assignable_v && + std::is_nothrow_move_constructible_v)>* = nullptr> + Expected& operator=(U&& v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~Unexpected(); + new (valptr()) T{std::forward(v)}; + this->m_has_val = true; + } + + return *this; + } + + template >* = nullptr, + std::enable_if_t<( + !std::is_same_v, std::remove_cvref_t> && + !std::conjunction_v, std::is_same>> && + std::is_constructible_v && std::is_assignable_v && + std::is_nothrow_move_constructible_v)>* = nullptr> + Expected& operator=(U&& v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~Unexpected(); + new (valptr()) T{std::forward(v)}; + this->m_has_val = true; + } + + return *this; + } + + template && + std::is_assignable_v>* = nullptr> + Expected& operator=(const Unexpected& rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + new (errptr()) Unexpected{rhs}; + this->m_has_val = false; + } + + return *this; + } + + template && + std::is_move_assignable_v>* = nullptr> + Expected& operator=(Unexpected&& rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + new (errptr()) Unexpected{std::move(rhs)}; + this->m_has_val = false; + } + + return *this; + } + + template >* = nullptr> + void emplace(Args&&... args) { + if (has_value()) { + val() = T{std::forward(args)...}; + } else { + err().~Unexpected(); + new (valptr()) T{std::forward(args)...}; + this->m_has_val = true; + } + } + + template >* = nullptr> + void emplace(Args&&... args) { + if (has_value()) { + val() = T{std::forward(args)...}; + } else { + auto tmp = std::move(err()); + err().~Unexpected(); + new (valptr()) T{std::forward(args)...}; + this->m_has_val = true; + } + } + + template &, + Args&&...>>* = nullptr> + void emplace(std::initializer_list il, Args&&... args) { + if (has_value()) { + T t{il, std::forward(args)...}; + val() = std::move(t); + } else { + err().~Unexpected(); + new (valptr()) T{il, std::forward(args)...}; + this->m_has_val = true; + } + } + + template &, + Args&&...>>* = nullptr> + void emplace(std::initializer_list il, Args&&... args) { + if (has_value()) { + T t{il, std::forward(args)...}; + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~Unexpected(); + new (valptr()) T{il, std::forward(args)...}; + this->m_has_val = true; + } + } + + constexpr T* operator->() { + return valptr(); + } + + constexpr const T* operator->() const { + return valptr(); + } + + template + constexpr U& operator*() & { + return val(); + } + + template + constexpr const U& operator*() const& { + return val(); + } + + template + constexpr U&& operator*() && { + return std::move(val()); + } + + template + constexpr const U&& operator*() const&& { + return std::move(val()); + } + + constexpr bool has_value() const noexcept { + return this->m_has_val; + } + + constexpr explicit operator bool() const noexcept { + return this->m_has_val; + } + + template + constexpr U& value() & { + return val(); + } + + template + constexpr const U& value() const& { + return val(); + } + + template + constexpr U&& value() && { + return std::move(val()); + } + + template + constexpr const U&& value() const&& { + return std::move(val()); + } + + constexpr E& error() & { + return err().value(); + } + + constexpr const E& error() const& { + return err().value(); + } + + constexpr E&& error() && { + return std::move(err().value()); + } + + constexpr const E&& error() const&& { + return std::move(err().value()); + } + + template + constexpr T value_or(U&& v) const& { + static_assert(std::is_copy_constructible_v && std::is_convertible_v, + "T must be copy-constructible and convertible from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + + template + constexpr T value_or(U&& v) && { + static_assert(std::is_move_constructible_v && std::is_convertible_v, + "T must be move-constructible and convertible from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } + +private: + static_assert(!std::is_reference_v, "T must not be a reference"); + static_assert(!std::is_same_v>, + "T must not be std::in_place_t"); + static_assert(!std::is_same_v>, "T must not be unexpect_t"); + static_assert(!std::is_same_v>>, + "T must not be Unexpected"); + static_assert(!std::is_reference_v, "E must not be a reference"); + + T* valptr() { + return std::addressof(this->m_val); + } + + const T* valptr() const { + return std::addressof(this->m_val); + } + + Unexpected* errptr() { + return std::addressof(this->m_unexpect); + } + + const Unexpected* errptr() const { + return std::addressof(this->m_unexpect); + } + + template + constexpr U& val() { + return this->m_val; + } + + template + constexpr const U& val() const { + return this->m_val; + } + + constexpr Unexpected& err() { + return this->m_unexpect; + } + + constexpr const Unexpected& err() const { + return this->m_unexpect; + } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; +}; + +template +constexpr bool operator==(const Expected& lhs, const Expected& rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} + +template +constexpr bool operator!=(const Expected& lhs, const Expected& rhs) { + return !operator==(lhs, rhs); +} + +template +constexpr bool operator==(const Expected& x, const U& v) { + return x.has_value() ? *x == v : false; +} + +template +constexpr bool operator==(const U& v, const Expected& x) { + return x.has_value() ? *x == v : false; +} + +template +constexpr bool operator!=(const Expected& x, const U& v) { + return !operator==(x, v); +} + +template +constexpr bool operator!=(const U& v, const Expected& x) { + return !operator==(v, x); +} + +template +constexpr bool operator==(const Expected& x, const Unexpected& e) { + return x.has_value() ? false : x.error() == e.value(); +} + +template +constexpr bool operator==(const Unexpected& e, const Expected& x) { + return x.has_value() ? false : x.error() == e.value(); +} + +template +constexpr bool operator!=(const Expected& x, const Unexpected& e) { + return !operator==(x, e); +} + +template +constexpr bool operator!=(const Unexpected& e, const Expected& x) { + return !operator==(e, x); +} + +} // namespace Common diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 638c6cea8..3d9ce863b 100755 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -39,13 +39,12 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw_file) { ResultVal RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const { if (!updatable) { - return MakeResult(file); + return file; } const PatchManager patch_manager{current_process_title_id, filesystem_controller, content_provider}; - return MakeResult( - patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); + return patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw); } ResultVal RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecordType type) const { @@ -58,8 +57,7 @@ ResultVal RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecor const PatchManager patch_manager{title_id, filesystem_controller, content_provider}; - return MakeResult( - patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), type)); + return patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), type); } ResultVal RomFSFactory::OpenPatchedRomFSWithProgramIndex( @@ -83,7 +81,7 @@ ResultVal RomFSFactory::Open(u64 title_id, StorageId storage, return ResultUnknown; } - return MakeResult(romfs); + return romfs; } std::shared_ptr RomFSFactory::GetEntry(u64 title_id, StorageId storage, diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index b5254dd75..0c8ec4ba2 100755 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -94,7 +94,7 @@ ResultVal SaveDataFactory::Create(SaveDataSpaceId space, return ResultUnknown; } - return MakeResult(std::move(out)); + return out; } ResultVal SaveDataFactory::Open(SaveDataSpaceId space, @@ -115,7 +115,7 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, return ResultUnknown; } - return MakeResult(std::move(out)); + return out; } VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const { diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index e5c72cd4d..c0e13e56f 100755 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -25,7 +25,7 @@ SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_) SDMCFactory::~SDMCFactory() = default; ResultVal SDMCFactory::Open() const { - return MakeResult(sd_dir); + return sd_dir; } VirtualDir SDMCFactory::GetSDMCModificationLoadRoot(u64 title_id) const { diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h index 3511f50a0..5b5b420bb 100755 --- a/src/core/hid/motion_input.h +++ b/src/core/hid/motion_input.h @@ -74,7 +74,7 @@ private: // Minimum gyro amplitude to detect if the device is moving f32 gyro_threshold = 0.0f; - // Number of invalid secuential data + // Number of invalid sequential data u32 reset_counter = 0; // If the provided data is invalid the device will be autocalibrated diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 5e0b620c2..526b87241 100755 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -859,7 +859,7 @@ ResultVal KPageTable::SetHeapSize(std::size_t size) { current_heap_addr = heap_region_start + size; } - return MakeResult(heap_region_start); + return heap_region_start; } ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, @@ -893,7 +893,7 @@ ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, block_manager->Update(addr, needed_num_pages, state, perm); - return MakeResult(addr); + return addr; } ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 2c6b24848..3807b9aa8 100755 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -4,11 +4,10 @@ #pragma once -#include -#include #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" +#include "common/expected.h" // All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes @@ -155,204 +154,131 @@ constexpr ResultCode ResultSuccess(0); constexpr ResultCode ResultUnknown(UINT32_MAX); /** - * This is an optional value type. It holds a `ResultCode` and, if that code is a success code, - * also holds a result of type `T`. If the code is an error code then trying to access the inner - * value fails, thus ensuring that the ResultCode of functions is always checked properly before - * their return value is used. It is similar in concept to the `std::optional` type - * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in - * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html). + * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it + * also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying + * to access the inner value with operator* is undefined behavior and will assert with Unwrap(). + * Users of this class must be cognizant to check the status of the ResultVal with operator bool(), + * Code(), Succeeded() or Failed() prior to accessing the inner value. * * An example of how it could be used: * \code * ResultVal Frobnicate(float strength) { * if (strength < 0.f || strength > 1.0f) { * // Can't frobnicate too weakly or too strongly - * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common, - * ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + * return ResultCode{ErrorModule::Common, 1}; * } else { * // Frobnicated! Give caller a cookie - * return MakeResult(42); + * return 42; * } * } * \endcode * * \code - * ResultVal frob_result = Frobnicate(0.75f); + * auto frob_result = Frobnicate(0.75f); * if (frob_result) { * // Frobbed ok * printf("My cookie is %d\n", *frob_result); * } else { - * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex); + * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.Code().raw); * } * \endcode */ template class ResultVal { public: - /// Constructs an empty `ResultVal` with the given error code. The code must not be a success - /// code. - ResultVal(ResultCode error_code = ResultUnknown) : result_code(error_code) { - ASSERT(error_code.IsError()); - } + constexpr ResultVal() : expected{} {} - /** - * Similar to the non-member function `MakeResult`, with the exception that you can manually - * specify the success code. `success_code` must not be an error code. - */ - template - [[nodiscard]] static ResultVal WithCode(ResultCode success_code, Args&&... args) { - ResultVal result; - result.emplace(success_code, std::forward(args)...); - return result; - } + constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} - ResultVal(const ResultVal& o) noexcept : result_code(o.result_code) { - if (!o.empty()) { - new (&object) T(o.object); - } - } - - ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) { - if (!o.empty()) { - new (&object) T(std::move(o.object)); - } - } - - ~ResultVal() { - if (!empty()) { - object.~T(); - } - } - - ResultVal& operator=(const ResultVal& o) noexcept { - if (this == &o) { - return *this; - } - if (!empty()) { - if (!o.empty()) { - object = o.object; - } else { - object.~T(); - } - } else { - if (!o.empty()) { - new (&object) T(o.object); - } - } - result_code = o.result_code; - - return *this; - } - - ResultVal& operator=(ResultVal&& o) noexcept { - if (this == &o) { - return *this; - } - if (!empty()) { - if (!o.empty()) { - object = std::move(o.object); - } else { - object.~T(); - } - } else { - if (!o.empty()) { - new (&object) T(std::move(o.object)); - } - } - result_code = o.result_code; - - return *this; - } - - /** - * Replaces the current result with a new constructed result value in-place. The code must not - * be an error code. - */ - template - void emplace(ResultCode success_code, Args&&... args) { - ASSERT(success_code.IsSuccess()); - if (!empty()) { - object.~T(); - } - new (&object) T(std::forward(args)...); - result_code = success_code; - } - - /// Returns true if the `ResultVal` contains an error code and no value. - [[nodiscard]] bool empty() const { - return result_code.IsError(); - } - - /// Returns true if the `ResultVal` contains a return value. - [[nodiscard]] bool Succeeded() const { - return result_code.IsSuccess(); - } - /// Returns true if the `ResultVal` contains an error code and no value. - [[nodiscard]] bool Failed() const { - return empty(); - } - - [[nodiscard]] ResultCode Code() const { - return result_code; - } - - [[nodiscard]] const T& operator*() const { - return object; - } - [[nodiscard]] T& operator*() { - return object; - } - [[nodiscard]] const T* operator->() const { - return &object; - } - [[nodiscard]] T* operator->() { - return &object; - } - - /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. template - [[nodiscard]] T ValueOr(U&& value) const { - return !empty() ? object : std::move(value); + constexpr ResultVal(U&& val) : expected{std::forward(val)} {} + + template + constexpr ResultVal(Args&&... args) : expected{std::in_place, std::forward(args)...} {} + + ~ResultVal() = default; + + constexpr ResultVal(const ResultVal&) = default; + constexpr ResultVal(ResultVal&&) = default; + + ResultVal& operator=(const ResultVal&) = default; + ResultVal& operator=(ResultVal&&) = default; + + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return expected.has_value(); } - /// Asserts that the result succeeded and returns a reference to it. - [[nodiscard]] T& Unwrap() & { - ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); - return **this; + [[nodiscard]] constexpr ResultCode Code() const { + return expected.has_value() ? ResultSuccess : expected.error(); } - [[nodiscard]] T&& Unwrap() && { + [[nodiscard]] constexpr bool Succeeded() const { + return expected.has_value(); + } + + [[nodiscard]] constexpr bool Failed() const { + return !expected.has_value(); + } + + [[nodiscard]] constexpr T* operator->() { + return std::addressof(expected.value()); + } + + [[nodiscard]] constexpr const T* operator->() const { + return std::addressof(expected.value()); + } + + [[nodiscard]] constexpr T& operator*() & { + return *expected; + } + + [[nodiscard]] constexpr const T& operator*() const& { + return *expected; + } + + [[nodiscard]] constexpr T&& operator*() && { + return *expected; + } + + [[nodiscard]] constexpr const T&& operator*() const&& { + return *expected; + } + + [[nodiscard]] constexpr T& Unwrap() & { ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); - return std::move(**this); + return expected.value(); + } + + [[nodiscard]] constexpr const T& Unwrap() const& { + ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); + return expected.value(); + } + + [[nodiscard]] constexpr T&& Unwrap() && { + ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); + return std::move(expected.value()); + } + + [[nodiscard]] constexpr const T&& Unwrap() const&& { + ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); + return std::move(expected.value()); + } + + template + [[nodiscard]] constexpr T ValueOr(U&& v) const& { + return expected.value_or(v); + } + + template + [[nodiscard]] constexpr T ValueOr(U&& v) && { + return expected.value_or(v); } private: - // A union is used to allocate the storage for the value, while allowing us to construct and - // destruct it at will. - union { - T object; - }; - ResultCode result_code; + // TODO: Replace this with std::expected once it is standardized in the STL. + Common::Expected expected; }; -/** - * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct - * `T` with and creates a success `ResultVal` contained the constructed value. - */ -template -[[nodiscard]] ResultVal MakeResult(Args&&... args) { - return ResultVal::WithCode(ResultSuccess, std::forward(args)...); -} - -/** - * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just - * copy or move constructing. - */ -template -[[nodiscard]] ResultVal> MakeResult(Arg&& arg) { - return ResultVal>::WithCode(ResultSuccess, std::forward(arg)); -} - /** * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps * the contained value and assigns it to `target`, which can be either an l-value expression or a diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index f8f9e32f7..42e468ce2 100755 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -226,11 +226,10 @@ ResultVal VfsDirectoryServiceWrapper::OpenFile(const std:: } if (mode == FileSys::Mode::Append) { - return MakeResult( - std::make_shared(file, 0, file->GetSize())); + return std::make_shared(file, 0, file->GetSize()); } - return MakeResult(file); + return file; } ResultVal VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { @@ -240,7 +239,7 @@ ResultVal VfsDirectoryServiceWrapper::OpenDirectory(const s // TODO(DarkLordZach): Find a better error code for this return FileSys::ERROR_PATH_NOT_FOUND; } - return MakeResult(dir); + return dir; } ResultVal VfsDirectoryServiceWrapper::GetEntryType( @@ -252,12 +251,12 @@ ResultVal VfsDirectoryServiceWrapper::GetEntryType( auto filename = Common::FS::GetFilename(path); // TODO(Subv): Some games use the '/' path, find out what this means. if (filename.empty()) - return MakeResult(FileSys::EntryType::Directory); + return FileSys::EntryType::Directory; if (dir->GetFile(filename) != nullptr) - return MakeResult(FileSys::EntryType::File); + return FileSys::EntryType::File; if (dir->GetSubdirectory(filename) != nullptr) - return MakeResult(FileSys::EntryType::Directory); + return FileSys::EntryType::Directory; return FileSys::ERROR_PATH_NOT_FOUND; } @@ -270,7 +269,7 @@ ResultVal VfsDirectoryServiceWrapper::GetFileTimeStam if (GetEntryType(path).Failed()) { return FileSys::ERROR_PATH_NOT_FOUND; } - return MakeResult(dir->GetFileTimeStamp(Common::FS::GetFilename(path))); + return dir->GetFileTimeStamp(Common::FS::GetFilename(path)); } FileSystemController::FileSystemController(Core::System& system_) : system{system_} {} @@ -395,7 +394,7 @@ ResultVal FileSystemController::OpenSaveDataSpace( return FileSys::ERROR_ENTITY_NOT_FOUND; } - return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); + return save_data_factory->GetSaveDataSpaceDirectory(space); } ResultVal FileSystemController::OpenSDMC() const { @@ -421,7 +420,7 @@ ResultVal FileSystemController::OpenBISPartition( return FileSys::ERROR_INVALID_ARGUMENT; } - return MakeResult(std::move(part)); + return part; } ResultVal FileSystemController::OpenBISPartitionStorage( @@ -437,7 +436,7 @@ ResultVal FileSystemController::OpenBISPartitionStorage( return FileSys::ERROR_INVALID_ARGUMENT; } - return MakeResult(std::move(part)); + return part; } u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const { diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp index aa9d48c0c..48e133b48 100755 --- a/src/core/hle/service/glue/glue_manager.cpp +++ b/src/core/hle/service/glue/glue_manager.cpp @@ -26,7 +26,7 @@ ResultVal ARPManager::GetLaunchProperty(u64 title_id) return ERR_NOT_REGISTERED; } - return MakeResult(iter->second.launch); + return iter->second.launch; } ResultVal> ARPManager::GetControlProperty(u64 title_id) const { @@ -39,7 +39,7 @@ ResultVal> ARPManager::GetControlProperty(u64 title_id) const { return ERR_NOT_REGISTERED; } - return MakeResult>(iter->second.control); + return iter->second.control; } ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 24b7e4435..e4b97c1f6 100755 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -335,7 +335,7 @@ public: CASCADE_CODE(result); if (ValidateRegionForMap(page_table, addr, size)) { - return MakeResult(addr); + return addr; } } @@ -371,7 +371,7 @@ public: } if (ValidateRegionForMap(page_table, addr, size)) { - return MakeResult(addr); + return addr; } } diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 4fef2aea4..ca4ed35bb 100755 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -443,14 +443,14 @@ ResultVal> MiiManager::GetDefault(SourceFlag source_ std::vector result; if ((source_flag & SourceFlag::Default) == SourceFlag::None) { - return MakeResult(std::move(result)); + return result; } for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { result.emplace_back(BuildDefault(index), Source::Default); } - return MakeResult(std::move(result)); + return result; } ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 931b48f72..64ffc8572 100755 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -414,7 +414,7 @@ ResultVal IApplicationManagerInterface::GetApplicationDesiredLanguage( for (const auto lang : *priority_list) { const auto supported_flag = GetSupportedLanguageFlag(lang); if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) { - return MakeResult(static_cast(lang)); + return static_cast(lang); } } @@ -448,7 +448,7 @@ ResultVal IApplicationManagerInterface::ConvertApplicationLanguageToLanguag return ERR_APPLICATION_LANGUAGE_NOT_FOUND; } - return MakeResult(static_cast(*language_code)); + return static_cast(*language_code); } IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_) diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index ae4dc4a75..41abb146c 100755 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -87,7 +87,7 @@ ResultVal ServiceManager::GetServicePort(const std::string& name auto handler = it->second; port->GetServerPort().SetSessionHandler(std::move(handler)); - return MakeResult(port); + return port; } /** @@ -165,7 +165,7 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); - return MakeResult(session); + return session; } void SM::RegisterService(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp index ed4c06260..10f7d1461 100755 --- a/src/core/hle/service/spl/spl_module.cpp +++ b/src/core/hle/service/spl/spl_module.cpp @@ -120,40 +120,40 @@ ResultVal Module::Interface::GetConfigImpl(ConfigItem config_item) const { return ResultSecureMonitorNotImplemented; case ConfigItem::ExosphereApiVersion: // Get information about the current exosphere version. - return MakeResult((u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) | - (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) | - (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) | - (static_cast(HLE::ApiVersion::GetTargetFirmware()))); + return (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) | + (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) | + (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) | + (static_cast(HLE::ApiVersion::GetTargetFirmware())); case ConfigItem::ExosphereNeedsReboot: // We are executing, so we aren't in the process of rebooting. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereNeedsShutdown: // We are executing, so we aren't in the process of shutting down. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereGitCommitHash: // Get information about the current exosphere git commit hash. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereHasRcmBugPatch: // Get information about whether this unit has the RCM bug patched. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereBlankProdInfo: // Get whether this unit should simulate a "blanked" PRODINFO. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereAllowCalWrites: // Get whether this unit should allow writing to the calibration partition. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereEmummcType: // Get what kind of emummc this unit has active. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExospherePayloadAddress: // Gets the physical address of the reboot payload buffer, if one exists. return ResultSecureMonitorNotInitialized; case ConfigItem::ExosphereLogConfiguration: // Get the log configuration. - return MakeResult(u64{0}); + return u64{0}; case ConfigItem::ExosphereForceEnableUsb30: // Get whether usb 3.0 should be force-enabled. - return MakeResult(u64{0}); + return u64{0}; default: return ResultSecureMonitorInvalidArgument; } diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 6f2b4007b..c3f363eeb 100755 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1273,15 +1273,15 @@ private: static ResultVal ConvertScalingModeImpl(NintendoScaleMode mode) { switch (mode) { case NintendoScaleMode::None: - return MakeResult(ConvertedScaleMode::None); + return ConvertedScaleMode::None; case NintendoScaleMode::Freeze: - return MakeResult(ConvertedScaleMode::Freeze); + return ConvertedScaleMode::Freeze; case NintendoScaleMode::ScaleToWindow: - return MakeResult(ConvertedScaleMode::ScaleToWindow); + return ConvertedScaleMode::ScaleToWindow; case NintendoScaleMode::ScaleAndCrop: - return MakeResult(ConvertedScaleMode::ScaleAndCrop); + return ConvertedScaleMode::ScaleAndCrop; case NintendoScaleMode::PreserveAspectRatio: - return MakeResult(ConvertedScaleMode::PreserveAspectRatio); + return ConvertedScaleMode::PreserveAspectRatio; default: LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); return ERR_OPERATION_FAILED; diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index 7230f85de..723455462 100755 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp @@ -433,15 +433,33 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) { } } -size_t FindNextUnusedLocation(const std::bitset& used_locations, - size_t start_offset) { +size_t FindAndSetNextUnusedLocation(std::bitset& used_locations, + size_t& start_offset) { for (size_t location = start_offset; location < used_locations.size(); ++location) { if (!used_locations.test(location)) { + start_offset = location; + used_locations.set(location); return location; } } throw RuntimeError("Unable to get an unused location for legacy attribute"); } + +Id DefineLegacyInput(EmitContext& ctx, std::bitset& used_locations, + size_t& start_offset) { + const Id id{DefineInput(ctx, ctx.F32[4], true)}; + const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset); + ctx.Decorate(id, spv::Decoration::Location, location); + return id; +} + +Id DefineLegacyOutput(EmitContext& ctx, std::bitset& used_locations, + size_t& start_offset, std::optional invocations) { + const Id id{DefineOutput(ctx, ctx.F32[4], invocations)}; + const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset); + ctx.Decorate(id, spv::Decoration::Location, location); + return id; +} } // Anonymous namespace void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { @@ -525,6 +543,64 @@ Id EmitContext::BitOffset16(const IR::Value& offset) { return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u)); } +Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) { + if (attribute >= IR::Attribute::ColorFrontDiffuseR && + attribute <= IR::Attribute::ColorFrontDiffuseA) { + return input_front_color; + } + if (attribute >= IR::Attribute::ColorFrontSpecularR && + attribute <= IR::Attribute::ColorFrontSpecularA) { + return input_front_secondary_color; + } + if (attribute >= IR::Attribute::ColorBackDiffuseR && + attribute <= IR::Attribute::ColorBackDiffuseA) { + return input_back_color; + } + if (attribute >= IR::Attribute::ColorBackSpecularR && + attribute <= IR::Attribute::ColorBackSpecularA) { + return input_back_secondary_color; + } + if (attribute == IR::Attribute::FogCoordinate) { + return input_fog_frag_coord; + } + if (attribute >= IR::Attribute::FixedFncTexture0S && + attribute <= IR::Attribute::FixedFncTexture9Q) { + u32 index = + (static_cast(attribute) - static_cast(IR::Attribute::FixedFncTexture0S)) / 4; + return input_fixed_fnc_textures[index]; + } + throw InvalidArgument("Attribute is not legacy attribute {}", attribute); +} + +Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) { + if (attribute >= IR::Attribute::ColorFrontDiffuseR && + attribute <= IR::Attribute::ColorFrontDiffuseA) { + return output_front_color; + } + if (attribute >= IR::Attribute::ColorFrontSpecularR && + attribute <= IR::Attribute::ColorFrontSpecularA) { + return output_front_secondary_color; + } + if (attribute >= IR::Attribute::ColorBackDiffuseR && + attribute <= IR::Attribute::ColorBackDiffuseA) { + return output_back_color; + } + if (attribute >= IR::Attribute::ColorBackSpecularR && + attribute <= IR::Attribute::ColorBackSpecularA) { + return output_back_secondary_color; + } + if (attribute == IR::Attribute::FogCoordinate) { + return output_fog_frag_coord; + } + if (attribute >= IR::Attribute::FixedFncTexture0S && + attribute <= IR::Attribute::FixedFncTexture9Q) { + u32 index = + (static_cast(attribute) - static_cast(IR::Attribute::FixedFncTexture0S)) / 4; + return output_fixed_fnc_textures[index]; + } + throw InvalidArgument("Attribute is not legacy attribute {}", attribute); +} + void EmitContext::DefineCommonTypes(const Info& info) { void_id = TypeVoid(); @@ -1353,22 +1429,26 @@ void EmitContext::DefineInputs(const IR::Program& program) { } size_t previous_unused_location = 0; if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) { - const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location); - previous_unused_location = location; - used_locations.set(location); - const Id id{DefineInput(*this, F32[4], true)}; - Decorate(id, spv::Decoration::Location, location); - input_front_color = id; + input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location); + } + if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) { + input_front_secondary_color = + DefineLegacyInput(*this, used_locations, previous_unused_location); + } + if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) { + input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location); + } + if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) { + input_back_secondary_color = + DefineLegacyInput(*this, used_locations, previous_unused_location); + } + if (loads.AnyComponent(IR::Attribute::FogCoordinate)) { + input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location); } for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) { if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) { - const size_t location = - FindNextUnusedLocation(used_locations, previous_unused_location); - previous_unused_location = location; - used_locations.set(location); - const Id id{DefineInput(*this, F32[4], true)}; - Decorate(id, spv::Decoration::Location, location); - input_fixed_fnc_textures[index] = id; + input_fixed_fnc_textures[index] = + DefineLegacyInput(*this, used_locations, previous_unused_location); } } if (stage == Stage::TessellationEval) { @@ -1430,22 +1510,29 @@ void EmitContext::DefineOutputs(const IR::Program& program) { } size_t previous_unused_location = 0; if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) { - const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location); - previous_unused_location = location; - used_locations.set(location); - const Id id{DefineOutput(*this, F32[4], invocations)}; - Decorate(id, spv::Decoration::Location, static_cast(location)); - output_front_color = id; + output_front_color = + DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations); + } + if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) { + output_front_secondary_color = + DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations); + } + if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) { + output_back_color = + DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations); + } + if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) { + output_back_secondary_color = + DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations); + } + if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) { + output_fog_frag_coord = + DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations); } for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) { if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) { - const size_t location = - FindNextUnusedLocation(used_locations, previous_unused_location); - previous_unused_location = location; - used_locations.set(location); - const Id id{DefineOutput(*this, F32[4], invocations)}; - Decorate(id, spv::Decoration::Location, location); - output_fixed_fnc_textures[index] = id; + output_fixed_fnc_textures[index] = + DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations); } } switch (stage) { diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index fb24490de..63f8185d9 100755 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h @@ -113,6 +113,9 @@ public: [[nodiscard]] Id BitOffset8(const IR::Value& offset); [[nodiscard]] Id BitOffset16(const IR::Value& offset); + Id InputLegacyAttribute(IR::Attribute attribute); + Id OutputLegacyAttribute(IR::Attribute attribute); + Id Const(u32 value) { return Constant(U32[1], value); } @@ -279,12 +282,20 @@ public: Id input_position{}; Id input_front_color{}; + Id input_front_secondary_color{}; + Id input_back_color{}; + Id input_back_secondary_color{}; + Id input_fog_frag_coord{}; std::array input_fixed_fnc_textures{}; std::array input_generics{}; Id output_point_size{}; Id output_position{}; Id output_front_color{}; + Id output_front_secondary_color{}; + Id output_back_color{}; + Id output_back_secondary_color{}; + Id output_fog_frag_coord{}; std::array output_fixed_fnc_textures{}; std::array, 32> output_generics{}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index b1e14ae1b..57e34292c 100755 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -43,23 +43,12 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... } } -bool IsFixedFncTexture(IR::Attribute attribute) { - return attribute >= IR::Attribute::FixedFncTexture0S && - attribute <= IR::Attribute::FixedFncTexture9Q; -} - -u32 FixedFncTextureAttributeIndex(IR::Attribute attribute) { - if (!IsFixedFncTexture(attribute)) { - throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute); - } - return (static_cast(attribute) - static_cast(IR::Attribute::FixedFncTexture0S)) / 4u; -} - -u32 FixedFncTextureAttributeElement(IR::Attribute attribute) { - if (!IsFixedFncTexture(attribute)) { - throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute); - } - return static_cast(attribute) % 4u; +bool IsLegacyAttribute(IR::Attribute attribute) { + return (attribute >= IR::Attribute::ColorFrontDiffuseR && + attribute <= IR::Attribute::ColorBackSpecularA) || + attribute == IR::Attribute::FogCoordinate || + (attribute >= IR::Attribute::FixedFncTexture0S && + attribute <= IR::Attribute::FixedFncTexture9Q); } template @@ -93,12 +82,16 @@ std::optional OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id); } } - if (IsFixedFncTexture(attr)) { - const u32 index{FixedFncTextureAttributeIndex(attr)}; - const u32 element{FixedFncTextureAttributeElement(attr)}; - const Id element_id{ctx.Const(element)}; - return OutputAccessChain(ctx, ctx.output_f32, ctx.output_fixed_fnc_textures[index], - element_id); + if (IsLegacyAttribute(attr)) { + if (attr == IR::Attribute::FogCoordinate) { + return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr), + ctx.Const(0u)); + } else { + const u32 element{static_cast(attr) % 4}; + const Id element_id{ctx.Const(element)}; + return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr), + element_id); + } } switch (attr) { case IR::Attribute::PointSize: @@ -111,14 +104,6 @@ std::optional OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { const Id element_id{ctx.Const(element)}; return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id); } - case IR::Attribute::ColorFrontDiffuseR: - case IR::Attribute::ColorFrontDiffuseG: - case IR::Attribute::ColorFrontDiffuseB: - case IR::Attribute::ColorFrontDiffuseA: { - const u32 element{static_cast(attr) % 4}; - const Id element_id{ctx.Const(element)}; - return OutputAccessChain(ctx, ctx.output_f32, ctx.output_front_color, element_id); - } case IR::Attribute::ClipDistance0: case IR::Attribute::ClipDistance1: case IR::Attribute::ClipDistance2: @@ -341,11 +326,18 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { const Id value{ctx.OpLoad(type->id, pointer)}; return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; } - if (IsFixedFncTexture(attr)) { - const u32 index{FixedFncTextureAttributeIndex(attr)}; - const Id attr_id{ctx.input_fixed_fnc_textures[index]}; - const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, attr_id, ctx.Const(element))}; - return ctx.OpLoad(ctx.F32[1], attr_ptr); + if (IsLegacyAttribute(attr)) { + if (attr == IR::Attribute::FogCoordinate) { + LOG_WARNING(Shader_SPIRV, "Get FogCoordinate Attribute called"); + const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, + ctx.InputLegacyAttribute(attr), ctx.Const(0u))}; + return ctx.OpLoad(ctx.F32[1], attr_ptr); + } else { + const Id element_id{ctx.Const(element)}; + const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, + ctx.InputLegacyAttribute(attr), element_id)}; + return ctx.OpLoad(ctx.F32[1], attr_ptr); + } } switch (attr) { case IR::Attribute::PrimitiveId: @@ -356,13 +348,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { case IR::Attribute::PositionW: return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.Const(element))); - case IR::Attribute::ColorFrontDiffuseR: - case IR::Attribute::ColorFrontDiffuseG: - case IR::Attribute::ColorFrontDiffuseB: - case IR::Attribute::ColorFrontDiffuseA: { - return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_front_color, - ctx.Const(element))); - } case IR::Attribute::InstanceId: if (ctx.profile.support_vertex_instance_id) { return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 3938c8c0e..9b516c64f 100755 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -10,16 +10,14 @@ #include #include #include + #include -#include "common/alignment.h" + #include "common/assert.h" #include "common/logging/log.h" #include "common/math_util.h" #include "common/microprofile.h" -#include "common/scope_exit.h" #include "common/settings.h" -#include "core/core.h" -#include "core/hle/kernel/k_process.h" #include "core/memory.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h"