diff --git a/README.md b/README.md index 91fbbf6b9..a0ba60386 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2000. +This is the source code for early-access 2003. ## Legal Notice diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 5851a9fed..8430b9778 100755 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -4,242 +4,175 @@ #pragma once +// a simple lockless thread-safe, +// single reader, single writer queue + #include #include -#include +#include #include -#include +#include namespace Common { - -/// a more foolproof multiple reader, multiple writer queue template -class MPMCQueue { -#define ABORT() \ - do { \ - std::cerr << __FILE__ " ERR " << __LINE__ << std::endl; \ - abort(); \ - } while (0) +class SPSCQueue { public: - ~MPMCQueue() { - Clear(); - if (waiting || head || tail) { - // Remove all the ABORT() after 1 month merged without problems - ABORT(); - } + SPSCQueue() { + write_ptr = read_ptr = new ElementPtr(); + } + ~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 void Push(Arg&& t) { - Node* const node = new Node(std::forward(t)); - if (!node || node == PLACEHOLDER) { - ABORT(); - } - while (true) { - if (Node* const previous = tail.load(ACQUIRE)) { - if (Node* exchange = nullptr; - !previous->next.compare_exchange_weak(exchange, node, ACQ_REL)) { - continue; - } - if (tail.exchange(node, ACQ_REL) != previous) { - ABORT(); - } - } else { - if (Node* exchange = nullptr; - !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(); - } + // create the element, add it to the queue + write_ptr->current = std::forward(t); + // set the next pointer to a new element ptr + // then advance the write pointer + ElementPtr* new_ptr = new ElementPtr(); + write_ptr->next.store(new_ptr, std::memory_order_release); + write_ptr = new_ptr; + ++size; + + // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the + // line before cv.wait + // 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. + std::lock_guard lock{cv_mutex}; + cv.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) { - return PopImpl(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() { + Wait(); T t; - if (!PopImpl(t)) { - ABORT(); - } + Pop(t); return t; } - void Wait() { - if (head.load(ACQUIRE)) { - return; - } - static_cast(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(); - } - } - + // not thread-safe void Clear() { - while (true) { - Node* const last = tail.load(ACQUIRE); - if (!last) { - 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; - } + size.store(0); + delete read_ptr; + write_ptr = read_ptr = new ElementPtr(); } private: - template - bool PopImpl(T& t) { - std::optional> lock{std::nullopt}; - while (true) { - Node* const node = head.load(ACQUIRE); - if (!node) { - if constexpr (!WAIT) { - return false; - } - if (!lock) { - static_cast(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; + // stores a pointer to element + // and a pointer to the next ElementPtr + class ElementPtr { + public: + ElementPtr() {} + ~ElementPtr() { + ElementPtr* next_ptr = next.load(); + + if (next_ptr) + delete next_ptr; } - } - struct Node { - template - explicit Node(Arg&& t) : value{std::forward(t)} {} - - Node(const Node&) = delete; - Node& operator=(const Node&) = delete; - - Node(Node&&) = delete; - Node& operator=(Node&&) = delete; - - const T value; - std::atomic next{nullptr}; + T current; + std::atomic next{nullptr}; }; - // We only need to avoid SEQ_CST on X86 - // We can add RELAXED later if we port to ARM and it's too slow - static constexpr auto ACQUIRE = std::memory_order_acquire; - static constexpr auto ACQ_REL = std::memory_order_acq_rel; - static inline const auto PLACEHOLDER = reinterpret_cast(1); - - std::atomic head{nullptr}; - std::atomic tail{nullptr}; - - std::atomic_size_t waiting{0}; - std::condition_variable condition{}; - std::mutex mutex{}; -#undef ABORT + ElementPtr* write_ptr; + ElementPtr* read_ptr; + std::atomic_size_t size{0}; + std::mutex cv_mutex; + std::condition_variable cv; }; -/// a simple lockless thread-safe, -/// single reader, single writer queue +// a simple thread-safe, +// single reader, multiple writer queue + template -class /*[[deprecated("Transition to MPMCQueue")]]*/ SPSCQueue { +class MPSCQueue { 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 void Push(Arg&& t) { - queue.Push(std::forward(t)); + std::lock_guard lock{write_lock}; + spsc_queue.Push(t); + } + + void Pop() { + return spsc_queue.Pop(); } bool Pop(T& t) { - return queue.Pop(t); + return spsc_queue.Pop(t); } void Wait() { - queue.Wait(); + spsc_queue.Wait(); } T PopWait() { - return queue.PopWait(); + return spsc_queue.PopWait(); } + // not thread-safe void Clear() { - queue.Clear(); + spsc_queue.Clear(); } private: - MPMCQueue queue{}; -}; - -/// a simple thread-safe, -/// single reader, multiple writer queue -template -class /*[[deprecated("Transition to MPMCQueue")]]*/ MPSCQueue { -public: - template - void Push(Arg&& t) { - queue.Push(std::forward(t)); - } - - bool Pop(T& t) { - return queue.Pop(t); - } - - T PopWait() { - return queue.PopWait(); - } - -private: - MPMCQueue queue{}; + SPSCQueue spsc_queue; + std::mutex write_lock; }; } // namespace Common diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index ef6854d62..36a4aa9cd 100755 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -16,6 +16,30 @@ namespace Service::AM::Applets { +struct ErrorCode { + u32 error_category{}; + u32 error_number{}; + + static constexpr ErrorCode FromU64(u64 error_code) { + return { + .error_category{static_cast(error_code >> 32)}, + .error_number{static_cast(error_code & 0xFFFFFFFF)}, + }; + } + + static constexpr ErrorCode FromResultCode(ResultCode result) { + return { + .error_category{2000 + static_cast(result.module.Value())}, + .error_number{result.description.Value()}, + }; + } + + constexpr ResultCode ToResultCode() const { + return ResultCode{static_cast(error_category - 2000), error_number}; + } +}; +static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); + #pragma pack(push, 4) struct ShowError { u8 mode; @@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector& data, T& variable) { } ResultCode Decode64BitError(u64 error) { - const auto description = (error >> 32) & 0x1FFF; - auto module = error & 0x3FF; - if (module >= 2000) - module -= 2000; - module &= 0x1FF; - return {static_cast(module), static_cast(description)}; + return ErrorCode::FromU64(error).ToResultCode(); } } // Anonymous namespace 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 fb8c02a77..14c77f162 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 @@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { if (IR::IsGeneric(attr)) { const u32 index{IR::GenericAttributeIndex(attr)}; const std::optional type{AttrTypes(ctx, index)}; - if (!type) { - // Attribute is disabled + if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) { + // Attribute is disabled or varying component is not written 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 pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; const Id value{ctx.OpLoad(type->id, pointer)}; diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 169bceae5..da328d904 100755 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -664,18 +664,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen // Face buttons p.setPen(colors.outline); button_color = colors.button; - DrawCircleButton(p, face_center + QPoint(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 + QPoint(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[A], face_radius); + DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius); + DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius); + DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius); // Face buttons text p.setPen(colors.transparent); p.setBrush(colors.font); - DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); - DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); - DrawSymbol(p, face_center + QPoint(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, 0), Symbol::A, text_size); + DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size); + DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size); + DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size); // D-pad constants const QPointF dpad_center = center + QPoint(-171, 8); @@ -686,18 +686,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen // D-pad buttons p.setPen(colors.outline); button_color = colors.button; - DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); - DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); - DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); - DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); + DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight], + dpad_radius); + DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], 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 p.setPen(colors.font2); p.setBrush(colors.font2); - DrawArrow(p, dpad_center + QPoint(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 + QPoint(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::Right, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size); // ZL and ZR buttons p.setPen(colors.outline);