early-access version 2003

This commit is contained in:
pineappleEA 2021-08-20 02:44:09 +02:00
parent dde6e66bd1
commit 07a248f589
5 changed files with 166 additions and 216 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 2000. This is the source code for early-access 2003.
## Legal Notice ## Legal Notice

View file

@ -4,242 +4,175 @@
#pragma once #pragma once
// a simple lockless thread-safe,
// single reader, single writer queue
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <iostream> #include <cstddef>
#include <mutex> #include <mutex>
#include <optional> #include <utility>
namespace Common { namespace Common {
/// a more foolproof multiple reader, multiple writer queue
template <typename T> template <typename T>
class MPMCQueue { class SPSCQueue {
#define ABORT() \
do { \
std::cerr << __FILE__ " ERR " << __LINE__ << std::endl; \
abort(); \
} while (0)
public: public:
~MPMCQueue() { SPSCQueue() {
Clear(); write_ptr = read_ptr = new ElementPtr();
if (waiting || head || tail) {
// Remove all the ABORT() after 1 month merged without problems
ABORT();
} }
~SPSCQueue() {
// this will empty out the whole queue
delete read_ptr;
}
[[nodiscard]] std::size_t Size() const {
return size.load();
}
[[nodiscard]] bool Empty() const {
return Size() == 0;
}
[[nodiscard]] T& Front() const {
return read_ptr->current;
} }
template <typename Arg> template <typename Arg>
void Push(Arg&& t) { void Push(Arg&& t) {
Node* const node = new Node(std::forward<Arg>(t)); // create the element, add it to the queue
if (!node || node == PLACEHOLDER) { write_ptr->current = std::forward<Arg>(t);
ABORT(); // set the next pointer to a new element ptr
} // then advance the write pointer
while (true) { ElementPtr* new_ptr = new ElementPtr();
if (Node* const previous = tail.load(ACQUIRE)) { write_ptr->next.store(new_ptr, std::memory_order_release);
if (Node* exchange = nullptr; write_ptr = new_ptr;
!previous->next.compare_exchange_weak(exchange, node, ACQ_REL)) { ++size;
continue;
} // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
if (tail.exchange(node, ACQ_REL) != previous) { // line before cv.wait
ABORT(); // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
} // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
} else { std::lock_guard lock{cv_mutex};
if (Node* exchange = nullptr; cv.notify_one();
!tail.compare_exchange_weak(exchange, node, ACQ_REL)) {
continue;
}
for (Node* exchange = nullptr;
!head.compare_exchange_weak(exchange, node, ACQ_REL);)
;
}
break;
}
if (waiting.load(ACQUIRE)) {
std::lock_guard lock{mutex};
condition.notify_one();
} }
void Pop() {
--size;
ElementPtr* tmpptr = read_ptr;
// advance the read pointer
read_ptr = tmpptr->next.load();
// set the next element to nullptr to stop the recursive deletion
tmpptr->next.store(nullptr);
delete tmpptr; // this also deletes the element
} }
bool Pop(T& t) { bool Pop(T& t) {
return PopImpl<false>(t); if (Empty())
return false;
--size;
ElementPtr* tmpptr = read_ptr;
read_ptr = tmpptr->next.load(std::memory_order_acquire);
t = std::move(tmpptr->current);
tmpptr->next.store(nullptr);
delete tmpptr;
return true;
}
void Wait() {
if (Empty()) {
std::unique_lock lock{cv_mutex};
cv.wait(lock, [this]() { return !Empty(); });
}
} }
T PopWait() { T PopWait() {
Wait();
T t; T t;
if (!PopImpl<true>(t)) { Pop(t);
ABORT();
}
return t; return t;
} }
void Wait() { // not thread-safe
if (head.load(ACQUIRE)) {
return;
}
static_cast<void>(waiting.fetch_add(1, ACQ_REL));
std::unique_lock lock{mutex};
while (true) {
if (head.load(ACQUIRE)) {
break;
}
condition.wait(lock);
}
if (!waiting.fetch_sub(1, ACQ_REL)) {
ABORT();
}
}
void Clear() { void Clear() {
while (true) { size.store(0);
Node* const last = tail.load(ACQUIRE); delete read_ptr;
if (!last) { write_ptr = read_ptr = new ElementPtr();
return;
}
if (Node* exchange = nullptr;
!last->next.compare_exchange_weak(exchange, PLACEHOLDER, ACQ_REL)) {
continue;
}
if (tail.exchange(nullptr, ACQ_REL) != last) {
ABORT();
}
Node* node = head.exchange(nullptr, ACQ_REL);
while (node && node != PLACEHOLDER) {
Node* next = node->next.load(ACQUIRE);
delete node;
node = next;
}
return;
}
} }
private: private:
template <bool WAIT> // stores a pointer to element
bool PopImpl(T& t) { // and a pointer to the next ElementPtr
std::optional<std::unique_lock<std::mutex>> lock{std::nullopt}; class ElementPtr {
while (true) { public:
Node* const node = head.load(ACQUIRE); ElementPtr() {}
if (!node) { ~ElementPtr() {
if constexpr (!WAIT) { ElementPtr* next_ptr = next.load();
return false;
} if (next_ptr)
if (!lock) { delete next_ptr;
static_cast<void>(waiting.fetch_add(1, ACQ_REL));
lock = std::unique_lock{mutex};
continue;
}
condition.wait(*lock);
continue;
}
Node* const next = node->next.load(ACQUIRE);
if (next) {
if (next == PLACEHOLDER) {
continue;
}
if (Node* exchange = node; !head.compare_exchange_weak(exchange, next, ACQ_REL)) {
continue;
}
} else {
if (Node* exchange = nullptr;
!node->next.compare_exchange_weak(exchange, PLACEHOLDER, ACQ_REL)) {
continue;
}
if (tail.exchange(nullptr, ACQ_REL) != node) {
ABORT();
}
if (head.exchange(nullptr, ACQ_REL) != node) {
ABORT();
}
}
t = std::move(node->value);
delete node;
if (lock) {
if (!waiting.fetch_sub(1, ACQ_REL)) {
ABORT();
}
}
return true;
}
} }
struct Node { T current;
template <typename Arg> std::atomic<ElementPtr*> next{nullptr};
explicit Node(Arg&& t) : value{std::forward<Arg>(t)} {}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = delete;
Node& operator=(Node&&) = delete;
const T value;
std::atomic<Node*> next{nullptr};
}; };
// We only need to avoid SEQ_CST on X86 ElementPtr* write_ptr;
// We can add RELAXED later if we port to ARM and it's too slow ElementPtr* read_ptr;
static constexpr auto ACQUIRE = std::memory_order_acquire; std::atomic_size_t size{0};
static constexpr auto ACQ_REL = std::memory_order_acq_rel; std::mutex cv_mutex;
static inline const auto PLACEHOLDER = reinterpret_cast<Node*>(1); std::condition_variable cv;
std::atomic<Node*> head{nullptr};
std::atomic<Node*> tail{nullptr};
std::atomic_size_t waiting{0};
std::condition_variable condition{};
std::mutex mutex{};
#undef ABORT
}; };
/// a simple lockless thread-safe, // a simple thread-safe,
/// single reader, single writer queue // single reader, multiple writer queue
template <typename T> template <typename T>
class /*[[deprecated("Transition to MPMCQueue")]]*/ SPSCQueue { class MPSCQueue {
public: public:
[[nodiscard]] std::size_t Size() const {
return spsc_queue.Size();
}
[[nodiscard]] bool Empty() const {
return spsc_queue.Empty();
}
[[nodiscard]] T& Front() const {
return spsc_queue.Front();
}
template <typename Arg> template <typename Arg>
void Push(Arg&& t) { void Push(Arg&& t) {
queue.Push(std::forward<Arg>(t)); std::lock_guard lock{write_lock};
spsc_queue.Push(t);
}
void Pop() {
return spsc_queue.Pop();
} }
bool Pop(T& t) { bool Pop(T& t) {
return queue.Pop(t); return spsc_queue.Pop(t);
} }
void Wait() { void Wait() {
queue.Wait(); spsc_queue.Wait();
} }
T PopWait() { T PopWait() {
return queue.PopWait(); return spsc_queue.PopWait();
} }
// not thread-safe
void Clear() { void Clear() {
queue.Clear(); spsc_queue.Clear();
} }
private: private:
MPMCQueue<T> queue{}; SPSCQueue<T> spsc_queue;
}; std::mutex write_lock;
/// a simple thread-safe,
/// single reader, multiple writer queue
template <typename T>
class /*[[deprecated("Transition to MPMCQueue")]]*/ MPSCQueue {
public:
template <typename Arg>
void Push(Arg&& t) {
queue.Push(std::forward<Arg>(t));
}
bool Pop(T& t) {
return queue.Pop(t);
}
T PopWait() {
return queue.PopWait();
}
private:
MPMCQueue<T> queue{};
}; };
} // namespace Common } // namespace Common

View file

@ -16,6 +16,30 @@
namespace Service::AM::Applets { namespace Service::AM::Applets {
struct ErrorCode {
u32 error_category{};
u32 error_number{};
static constexpr ErrorCode FromU64(u64 error_code) {
return {
.error_category{static_cast<u32>(error_code >> 32)},
.error_number{static_cast<u32>(error_code & 0xFFFFFFFF)},
};
}
static constexpr ErrorCode FromResultCode(ResultCode result) {
return {
.error_category{2000 + static_cast<u32>(result.module.Value())},
.error_number{result.description.Value()},
};
}
constexpr ResultCode ToResultCode() const {
return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
}
};
static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
#pragma pack(push, 4) #pragma pack(push, 4)
struct ShowError { struct ShowError {
u8 mode; u8 mode;
@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
} }
ResultCode Decode64BitError(u64 error) { ResultCode Decode64BitError(u64 error) {
const auto description = (error >> 32) & 0x1FFF; return ErrorCode::FromU64(error).ToResultCode();
auto module = error & 0x3FF;
if (module >= 2000)
module -= 2000;
module &= 0x1FF;
return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
} }
} // Anonymous namespace } // Anonymous namespace

View file

@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
if (IR::IsGeneric(attr)) { if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)}; const u32 index{IR::GenericAttributeIndex(attr)};
const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
if (!type) { if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
// Attribute is disabled // Attribute is disabled or varying component is not written
return ctx.Const(element == 3 ? 1.0f : 0.0f); return ctx.Const(element == 3 ? 1.0f : 0.0f);
} }
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
// Varying component is not written
return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
}
const Id generic_id{ctx.input_generics.at(index)}; const Id generic_id{ctx.input_generics.at(index)};
const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
const Id value{ctx.OpLoad(type->id, pointer)}; const Id value{ctx.OpLoad(type->id, pointer)};

View file

@ -664,18 +664,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
// Face buttons // Face buttons
p.setPen(colors.outline); p.setPen(colors.outline);
button_color = colors.button; button_color = colors.button;
DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); DrawCircleButton(p, face_center + QPointF(face_distance, 0), button_values[A], face_radius);
DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius);
DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius);
DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius);
// Face buttons text // Face buttons text
p.setPen(colors.transparent); p.setPen(colors.transparent);
p.setBrush(colors.font); p.setBrush(colors.font);
DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); DrawSymbol(p, face_center + QPointF(face_distance, 0), Symbol::A, text_size);
DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size);
DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size);
DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size);
// D-pad constants // D-pad constants
const QPointF dpad_center = center + QPoint(-171, 8); const QPointF dpad_center = center + QPoint(-171, 8);
@ -686,18 +686,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
// D-pad buttons // D-pad buttons
p.setPen(colors.outline); p.setPen(colors.outline);
button_color = colors.button; button_color = colors.button;
DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight],
DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); dpad_radius);
DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], dpad_radius);
DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); DrawCircleButton(p, dpad_center + QPointF(0, -dpad_distance), button_values[DUp], dpad_radius);
DrawCircleButton(p, dpad_center + QPointF(-dpad_distance, 0), button_values[DLeft],
dpad_radius);
// D-pad arrows // D-pad arrows
p.setPen(colors.font2); p.setPen(colors.font2);
p.setBrush(colors.font2); p.setBrush(colors.font2);
DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); DrawArrow(p, dpad_center + QPointF(dpad_distance, 0), Direction::Right, dpad_arrow_size);
DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size);
DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size);
DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size);
// ZL and ZR buttons // ZL and ZR buttons
p.setPen(colors.outline); p.setPen(colors.outline);