diff options
Diffstat (limited to 'src')
71 files changed, 958 insertions, 398 deletions
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 18fde8bd6..3bb111748 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -86,13 +86,13 @@ public: u32 num_domain_objects{}; const bool always_move_handles{ (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; - if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { + if (!ctx.GetManager()->IsDomain() || always_move_handles) { num_handles_to_move = num_objects_to_move; } else { num_domain_objects = num_objects_to_move; } - if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { + if (ctx.GetManager()->IsDomain()) { raw_data_size += static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); ctx.write_size += num_domain_objects; @@ -125,8 +125,7 @@ public: if (!ctx.IsTipc()) { AlignWithPadding(); - if (ctx.Session()->GetSessionRequestManager()->IsDomain() && - ctx.HasDomainMessageHeader()) { + if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); @@ -146,18 +145,18 @@ public: template <class T> void PushIpcInterface(std::shared_ptr<T> iface) { - if (context->Session()->GetSessionRequestManager()->IsDomain()) { + if (context->GetManager()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( Kernel::LimitableResource::Sessions, 1); auto* session = Kernel::KSession::Create(kernel); - session->Initialize(nullptr, iface->GetServiceName(), - std::make_shared<Kernel::SessionRequestManager>(kernel)); + session->Initialize(nullptr, iface->GetServiceName()); + iface->RegisterSession(&session->GetServerSession(), + std::make_shared<Kernel::SessionRequestManager>(kernel)); context->AddMoveObject(&session->GetClientSession()); - iface->ClientConnected(&session->GetServerSession()); } } @@ -387,7 +386,7 @@ public: template <class T> std::weak_ptr<T> PopIpcInterface() { - ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); + ASSERT(context->GetManager()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler<T>(Pop<u32>() - 1); } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e4f43a053..fd354d484 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -35,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se } SessionRequestHandler::~SessionRequestHandler() { - kernel.ReleaseServiceThread(service_thread); + kernel.ReleaseServiceThread(service_thread.lock()); +} + +void SessionRequestHandler::AcceptSession(KServerPort* server_port) { + auto* server_session = server_port->AcceptSession(); + ASSERT(server_session != nullptr); + + RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel)); +} + +void SessionRequestHandler::RegisterSession(KServerSession* server_session, + std::shared_ptr<SessionRequestManager> manager) { + manager->SetSessionHandler(shared_from_this()); + service_thread.lock()->RegisterServerSession(server_session, manager); + server_session->Close(); } SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} @@ -92,7 +107,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses } // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs - context.SetSessionRequestManager(server_session->GetSessionRequestManager()); + ASSERT(context.GetManager().get() == this); // If there is a DomainMessageHeader, then this is CommandType "Request" const auto& domain_message_header = context.GetDomainMessageHeader(); @@ -130,31 +145,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses return ResultSuccess; } -Result SessionRequestManager::QueueSyncRequest(KSession* parent, - std::shared_ptr<HLERequestContext>&& context) { - // Ensure we have a session request handler - if (this->HasSessionRequestHandler(*context)) { - if (auto strong_ptr = this->GetServiceThread().lock()) { - strong_ptr->QueueSyncRequest(*parent, std::move(context)); - } else { - ASSERT_MSG(false, "strong_ptr is nullptr!"); - } - } else { - ASSERT_MSG(false, "handler is invalid!"); - } - - return ResultSuccess; -} - -void SessionRequestHandler::ClientConnected(KServerSession* session) { - session->GetSessionRequestManager()->SetSessionHandler(shared_from_this()); - - // Ensure our server session is tracked globally. - kernel.RegisterServerObject(session); -} - -void SessionRequestHandler::ClientDisconnected(KServerSession* session) {} - HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, KServerSession* server_session_, KThread* thread_) : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} { @@ -214,7 +204,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 // Padding to align to 16 bytes rp.AlignWithPadding(); - if (Session()->GetSessionRequestManager()->IsDomain() && + if (GetManager()->IsDomain() && ((command_header->type == IPC::CommandType::Request || command_header->type == IPC::CommandType::RequestWithContext) || !incoming)) { @@ -223,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 if (incoming || domain_message_header) { domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); } else { - if (Session()->GetSessionRequestManager()->IsDomain()) { + if (GetManager()->IsDomain()) { LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } @@ -316,12 +306,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. - if (server_session->GetSessionRequestManager()->IsDomain()) { + if (GetManager()->IsDomain()) { current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); for (auto& object : outgoing_domain_objects) { - server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); - cmd_buf[current_offset++] = static_cast<u32_le>( - server_session->GetSessionRequestManager()->DomainHandlerCount()); + GetManager()->AppendDomainHandler(std::move(object)); + cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount()); } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 1083638a9..67da8e7e1 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -45,11 +45,13 @@ class KAutoObject; class KernelCore; class KEvent; class KHandleTable; +class KServerPort; class KProcess; class KServerSession; class KThread; class KReadableEvent; class KSession; +class SessionRequestManager; class ServiceThread; enum class ThreadWakeupReason; @@ -76,19 +78,9 @@ public: virtual Result HandleSyncRequest(Kernel::KServerSession& session, Kernel::HLERequestContext& context) = 0; - /** - * Signals that a client has just connected to this HLE handler and keeps the - * associated ServerSession alive for the duration of the connection. - * @param server_session Owning pointer to the ServerSession associated with the connection. - */ - void ClientConnected(KServerSession* session); - - /** - * Signals that a client has just disconnected from this HLE handler and releases the - * associated ServerSession. - * @param server_session ServerSession associated with the connection. - */ - void ClientDisconnected(KServerSession* session); + void AcceptSession(KServerPort* server_port); + void RegisterSession(KServerSession* server_session, + std::shared_ptr<SessionRequestManager> manager); std::weak_ptr<ServiceThread> GetServiceThread() const { return service_thread; @@ -170,7 +162,6 @@ public: Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); - Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context); private: bool convert_to_domain{}; @@ -350,11 +341,11 @@ public: template <typename T> std::shared_ptr<T> GetDomainHandler(std::size_t index) const { - return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); + return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock()); } void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { - manager = std::move(manager_); + manager = manager_; } std::string Description() const; @@ -363,6 +354,10 @@ public: return *thread; } + std::shared_ptr<SessionRequestManager> GetManager() const { + return manager.lock(); + } + private: friend class IPC::ResponseBuilder; @@ -396,7 +391,7 @@ private: u32 handles_offset{}; u32 domain_offset{}; - std::weak_ptr<SessionRequestManager> manager; + std::weak_ptr<SessionRequestManager> manager{}; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 3cb22ff4d..eaa2e094c 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -58,8 +58,7 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -Result KClientPort::CreateSession(KClientSession** out, - std::shared_ptr<SessionRequestManager> session_manager) { +Result KClientPort::CreateSession(KClientSession** out) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), LimitableResource::Sessions); @@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out, } // Initialize the session. - session->Initialize(this, parent->GetName(), session_manager); + session->Initialize(this, parent->GetName()); // Commit the session reservation. session_reservation.Commit(); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index e17eff28f..81046fb86 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -52,8 +52,7 @@ public: void Destroy() override; bool IsSignaled() const override; - Result CreateSession(KClientSession** out, - std::shared_ptr<SessionRequestManager> session_manager = nullptr); + Result CreateSession(KClientSession** out); private: std::atomic<s32> num_sessions{}; diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index 7a5a9dc2a..77d00ae2c 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) { server.EnqueueSession(session); - if (auto session_ptr = server.GetSessionRequestHandler().lock()) { - session_ptr->ClientConnected(server.AcceptSession()); - } else { - ASSERT(false); - } - return ResultSuccess; } diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index e968f26ad..16968ba97 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -61,12 +61,6 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); - - // Release host emulation members. - session_handler.reset(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } bool KServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index fd4f4bd20..5fc7ee683 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -27,24 +27,6 @@ public: void Initialize(KPort* parent_port_, std::string&& name_); - /// Whether or not this server port has an HLE handler available. - bool HasSessionRequestHandler() const { - return !session_handler.expired(); - } - - /// Gets the HLE handler for this port. - SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { - return session_handler; - } - - /** - * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port - * will inherit a reference to this handler. - */ - void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { - session_handler = std::move(handler); - } - void EnqueueSession(KServerSession* pending_session); KServerSession* AcceptSession(); @@ -65,7 +47,6 @@ private: void CleanupSessions(); SessionList session_list; - SessionRequestHandlerWeakPtr session_handler; KPort* parent{}; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index faf03fcc8..aa1941f01 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> @@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_) KServerSession::~KServerSession() = default; -void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr<SessionRequestManager> manager_) { +void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) { // Set member variables. parent = parent_session_; name = std::move(name_); - manager = manager_; } void KServerSession::Destroy() { @@ -47,18 +45,99 @@ void KServerSession::Destroy() { this->CleanupRequests(); parent->Close(); - - // Release host emulation members. - manager.reset(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } void KServerSession::OnClientClosed() { - if (manager && manager->HasSessionHandler()) { - manager->SessionHandler().ClientDisconnected(this); + KScopedLightLock lk{m_lock}; + + // Handle any pending requests. + KSessionRequest* prev_request = nullptr; + while (true) { + // Declare variables for processing the request. + KSessionRequest* request = nullptr; + KEvent* event = nullptr; + KThread* thread = nullptr; + bool cur_request = false; + bool terminate = false; + + // Get the next request. + { + KScopedSchedulerLock sl{kernel}; + + if (m_current_request != nullptr && m_current_request != prev_request) { + // Set the request, open a reference as we process it. + request = m_current_request; + request->Open(); + cur_request = true; + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + + // If the thread is terminating, handle that. + if (thread->IsTerminationRequested()) { + request->ClearThread(); + request->ClearEvent(); + terminate = true; + } + + prev_request = request; + } else if (!m_request_list.empty()) { + // Pop the request from the front of the list. + request = std::addressof(m_request_list.front()); + m_request_list.pop_front(); + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + } + } + + // If there are no requests, we're done. + if (request == nullptr) { + break; + } + + // All requests must have threads. + ASSERT(thread != nullptr); + + // Ensure that we close the request when done. + SCOPE_EXIT({ request->Close(); }); + + // If we're terminating, close a reference to the thread and event. + if (terminate) { + thread->Close(); + if (event != nullptr) { + event->Close(); + } + } + + // If we need to, reply. + if (event != nullptr && !cur_request) { + // There must be no mappings. + ASSERT(request->GetSendCount() == 0); + ASSERT(request->GetReceiveCount() == 0); + ASSERT(request->GetExchangeCount() == 0); + + // // Get the process and page table. + // KProcess *client_process = thread->GetOwnerProcess(); + // auto &client_pt = client_process->GetPageTable(); + + // // Reply to the request. + // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(), + // ResultSessionClosed); + + // // Unlock the buffer. + // // NOTE: Nintendo does not check the result of this. + // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize()); + + // Signal the event. + event->Signal(); + } } + + // Notify. + this->NotifyAvailable(ResultSessionClosed); } bool KServerSession::IsSignaled() const { @@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const { return !m_request_list.empty() && m_current_request == nullptr; } -Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { - u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; - auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread); - - context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); - - return manager->QueueSyncRequest(parent, std::move(context)); -} - -Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { - Result result = manager->CompleteSyncRequest(this, context); - - // The calling thread is waiting for this request to complete, so wake it up. - context.GetThread().EndWait(result); - - return result; -} - Result KServerSession::OnRequest(KSessionRequest* request) { // Create the wait queue. ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; @@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) { // Check that we're not terminating. R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); - if (manager) { - // HLE request. - auto& memory{kernel.System().Memory()}; - this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); - } else { - // Non-HLE request. - - // Get whether we're empty. - const bool was_empty = m_request_list.empty(); + // Get whether we're empty. + const bool was_empty = m_request_list.empty(); - // Add the request to the list. - request->Open(); - m_request_list.push_back(*request); + // Add the request to the list. + request->Open(); + m_request_list.push_back(*request); - // If we were empty, signal. - if (was_empty) { - this->NotifyAvailable(); - } + // If we were empty, signal. + if (was_empty) { + this->NotifyAvailable(); } // If we have a request event, this is asynchronous, and we don't need to wait. @@ -136,7 +189,7 @@ Result KServerSession::OnRequest(KSessionRequest* request) { return GetCurrentThread(kernel).GetWaitResult(); } -Result KServerSession::SendReply() { +Result KServerSession::SendReply(bool is_hle) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -171,13 +224,18 @@ Result KServerSession::SendReply() { Result result = ResultSuccess; if (!closed) { // If we're not closed, send the reply. - Core::Memory::Memory& memory{kernel.System().Memory()}; - KThread* server_thread{GetCurrentThreadPointer(kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + if (is_hle) { + // HLE servers write directly to a pointer to the thread command buffer. Therefore + // the reply has already been written in this case. + } else { + Core::Memory::Memory& memory{kernel.System().Memory()}; + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - auto* dst_msg_buffer = memory.GetPointer(client_message); - std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + auto* dst_msg_buffer = memory.GetPointer(client_message); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } } else { result = ResultSessionClosed; } @@ -223,7 +281,8 @@ Result KServerSession::SendReply() { return result; } -Result KServerSession::ReceiveRequest() { +Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context, + std::weak_ptr<SessionRequestManager> manager) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() { // Receive the message. Core::Memory::Memory& memory{kernel.System().Memory()}; - KThread* server_thread{GetCurrentThreadPointer(kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + if (out_context != nullptr) { + // HLE request. + u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))}; + *out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread); + (*out_context)->SetSessionRequestManager(manager); + (*out_context) + ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), + cmd_buf); + } else { + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(client_message); - auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + auto* src_msg_buffer = memory.GetPointer(client_message); + auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } // We succeeded. return ResultSuccess; diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 188aef4af..6e189af8b 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,21 +16,11 @@ #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" -namespace Core::Memory { -class Memory; -} - -namespace Core::Timing { -class CoreTiming; -struct EventType; -} // namespace Core::Timing - namespace Kernel { class HLERequestContext; class KernelCore; class KSession; -class SessionRequestHandler; class SessionRequestManager; class KThread; @@ -46,8 +36,7 @@ public: void Destroy() override; - void Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr<SessionRequestManager> manager_); + void Initialize(KSession* parent_session_, std::string&& name_); KSession* GetParent() { return parent; @@ -60,32 +49,20 @@ public: bool IsSignaled() const override; void OnClientClosed(); - /// Gets the session request manager, which forwards requests to the underlying service - std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() { - return manager; - } - /// TODO: flesh these out to match the real kernel Result OnRequest(KSessionRequest* request); - Result SendReply(); - Result ReceiveRequest(); + Result SendReply(bool is_hle = false); + Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr, + std::weak_ptr<SessionRequestManager> manager = {}); + + Result SendReplyHLE() { + return SendReply(true); + } private: /// Frees up waiting client sessions when this server session is about to die void CleanupRequests(); - /// Queues a sync request from the emulated application. - Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); - - /// Completes a sync request from the emulated application. - Result CompleteSyncRequest(HLERequestContext& context); - - /// This session's HLE request handlers; if nullptr, this is not an HLE server - std::shared_ptr<SessionRequestManager> manager; - - /// When set to True, converts the session to a domain at the end of the command - bool convert_to_domain{}; - /// KSession that owns this KServerSession KSession* parent{}; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index ee05aa282..7a6534ac3 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} KSession::~KSession() = default; -void KSession::Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr<SessionRequestManager> manager_) { +void KSession::Initialize(KClientPort* port_, const std::string& name_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed @@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_, KAutoObject::Create(std::addressof(client)); // Initialize our sub sessions. - server.Initialize(this, name_ + ":Server", manager_); + server.Initialize(this, name_ + ":Server"); client.Initialize(this, name_ + ":Client"); // Set state and name. diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index c6ead403b..93e5e6f71 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -21,8 +21,7 @@ public: explicit KSession(KernelCore& kernel_); ~KSession() override; - void Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr<SessionRequestManager> manager_ = nullptr); + void Initialize(KClientPort* port_, const std::string& name_); void Finalize() override; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index eda4e9e1c..47b760a9c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -67,7 +67,6 @@ struct KernelCore::Impl { global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); - default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; @@ -93,6 +92,8 @@ struct KernelCore::Impl { } RegisterHostThread(); + + default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); } void InitializeCores() { @@ -191,17 +192,6 @@ struct KernelCore::Impl { } void CloseServices() { - // Close all open server sessions and ports. - std::unordered_set<KAutoObject*> server_objects_; - { - std::scoped_lock lk(server_objects_lock); - server_objects_ = server_objects; - server_objects.clear(); - } - for (auto* server_object : server_objects_) { - server_object->Close(); - } - // Ensures all service threads gracefully shutdown. ClearServiceThreads(); } @@ -419,6 +409,8 @@ struct KernelCore::Impl { return this_id; } + static inline thread_local bool is_phantom_mode_for_singlecore{false}; + bool IsPhantomModeForSingleCore() const { return is_phantom_mode_for_singlecore; } @@ -775,24 +767,21 @@ struct KernelCore::Impl { return {}; } - KClientPort* port = &search->second(system.ServiceManager(), system); - RegisterServerObject(&port->GetParent()->GetServerPort()); - return port; + return &search->second(system.ServiceManager(), system); } - void RegisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.insert(server_object); - } + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + auto search = service_interface_handlers.find(name); + if (search == service_interface_handlers.end()) { + return; + } - void UnregisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.erase(server_object); + search->second(system.ServiceManager(), server_port); } std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, const std::string& name) { - auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); + auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name); service_threads_manager.QueueWork( [this, service_thread]() { service_threads.emplace(service_thread); }); @@ -822,7 +811,6 @@ struct KernelCore::Impl { service_thread_barrier.Sync(); } - std::mutex server_objects_lock; std::mutex registered_objects_lock; std::mutex registered_in_use_objects_lock; @@ -853,8 +841,8 @@ struct KernelCore::Impl { /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; + std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers; NamedPortTable named_ports; - std::unordered_set<KAutoObject*> server_objects; std::unordered_set<KAutoObject*> registered_objects; std::unordered_set<KAutoObject*> registered_in_use_objects; @@ -903,7 +891,6 @@ struct KernelCore::Impl { bool is_multicore{}; std::atomic_bool is_shutting_down{}; - bool is_phantom_mode_for_singlecore{}; u32 single_core_thread_id{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; @@ -1070,16 +1057,17 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory& impl->service_interface_factory.emplace(std::move(name), factory); } -KClientPort* KernelCore::CreateNamedServicePort(std::string name) { - return impl->CreateNamedServicePort(std::move(name)); +void KernelCore::RegisterInterfaceForNamedService(std::string name, + ServiceInterfaceHandlerFn&& handler) { + impl->service_interface_handlers.emplace(std::move(name), handler); } -void KernelCore::RegisterServerObject(KAutoObject* server_object) { - impl->RegisterServerObject(server_object); +KClientPort* KernelCore::CreateNamedServicePort(std::string name) { + return impl->CreateNamedServicePort(std::move(name)); } -void KernelCore::UnregisterServerObject(KAutoObject* server_object) { - impl->UnregisterServerObject(server_object); +void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + impl->RegisterNamedServiceHandler(std::move(name), server_port); } void KernelCore::RegisterKernelObject(KAutoObject* object) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2549503fc..caca60586 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -48,6 +48,7 @@ class KPort; class KProcess; class KResourceLimit; class KScheduler; +class KServerPort; class KServerSession; class KSession; class KSessionRequest; @@ -67,6 +68,8 @@ class TimeManager; using ServiceInterfaceFactory = std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; +using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>; + namespace Init { struct KSlabResourceCounts; } @@ -196,16 +199,14 @@ public: /// Registers a named HLE service, passing a factory used to open a port to that service. void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); + /// Registers a setup function for the named HLE service. + void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler); + /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); - /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. - /// This is necessary because we do not emulate processes for HLE sessions and ports. - void RegisterServerObject(KAutoObject* server_object); - - /// Unregisters a server session or port previously registered with RegisterServerSession when - /// it was destroyed during the current emulation session. - void UnregisterServerObject(KAutoObject* server_object); + /// Accepts a session on a port created by CreateNamedServicePort. + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port); /// Registers all kernel objects with the global emulation state, this is purely for tracking /// leaks after emulation has been shutdown. diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index d23d76706..c8fe42537 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -1,15 +1,18 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <condition_variable> #include <functional> +#include <map> #include <mutex> #include <thread> #include <vector> -#include <queue> #include "common/scope_exit.h" #include "common/thread.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -19,101 +22,198 @@ namespace Kernel { class ServiceThread::Impl final { public: - explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit Impl(KernelCore& kernel, const std::string& service_name); ~Impl(); - void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); + void WaitAndProcessImpl(); + void SessionClosed(KServerSession* server_session, + std::shared_ptr<SessionRequestManager> manager); + void LoopProcess(); + + void RegisterServerSession(KServerSession* session, + std::shared_ptr<SessionRequestManager> manager); private: - std::vector<std::jthread> threads; - std::queue<std::function<void()>> requests; - std::mutex queue_mutex; - std::condition_variable_any condition; - const std::string service_name; + KernelCore& kernel; + + std::jthread m_thread; + std::mutex m_session_mutex; + std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; + KEvent* m_wakeup_event; + KProcess* m_process; + std::atomic<bool> m_shutdown_requested; + const std::string m_service_name; }; -ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : service_name{name} { - for (std::size_t i = 0; i < num_threads; ++i) { - threads.emplace_back([this, &kernel](std::stop_token stop_token) { - Common::SetCurrentThreadName(std::string{service_name}.c_str()); +void ServiceThread::Impl::WaitAndProcessImpl() { + // Create local list of waitable sessions. + std::vector<KSynchronizationObject*> objs; + std::vector<std::shared_ptr<SessionRequestManager>> managers; - // Wait for first request before trying to acquire a render context - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); - } + { + // Lock to get the set. + std::scoped_lock lk{m_session_mutex}; - if (stop_token.stop_requested()) { - return; - } + // Reserve the needed quantity. + objs.reserve(m_sessions.size() + 1); + managers.reserve(m_sessions.size()); - // Allocate a dummy guest thread for this host thread. - kernel.RegisterHostThread(); + // Copy to our local list. + for (const auto& [session, manager] : m_sessions) { + objs.push_back(session); + managers.push_back(manager); + } - while (true) { - std::function<void()> task; + // Insert the wakeup event at the end. + objs.push_back(&m_wakeup_event->GetReadableEvent()); + } - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + // Wait on the list of sessions. + s32 index{-1}; + Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast<s32>(objs.size()), -1); + ASSERT(!rc.IsFailure()); + + // If this was the wakeup event, clear it and finish. + if (index >= static_cast<s64>(objs.size() - 1)) { + m_wakeup_event->Clear(); + return; + } - if (stop_token.stop_requested()) { - return; - } + // This event is from a server session. + auto* server_session = static_cast<KServerSession*>(objs[index]); + auto& manager = managers[index]; - if (requests.empty()) { - continue; - } + // Fetch the HLE request context. + std::shared_ptr<HLERequestContext> context; + rc = server_session->ReceiveRequest(&context, manager); - task = std::move(requests.front()); - requests.pop(); - } + // If the session was closed, handle that. + if (rc == ResultSessionClosed) { + SessionClosed(server_session, manager); - task(); - } - }); + // Finish. + return; } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + + // Perform the request. + Result service_rc = manager->CompleteSyncRequest(server_session, *context); + + // Reply to the client. + rc = server_session->SendReplyHLE(); + + if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { + SessionClosed(server_session, manager); + return; + } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + ASSERT(service_rc == ResultSuccess); } -void ServiceThread::Impl::QueueSyncRequest(KSession& session, - std::shared_ptr<HLERequestContext>&& context) { +void ServiceThread::Impl::SessionClosed(KServerSession* server_session, + std::shared_ptr<SessionRequestManager> manager) { { - std::unique_lock lock{queue_mutex}; + // Lock to get the set. + std::scoped_lock lk{m_session_mutex}; + + // Erase the session. + ASSERT(m_sessions.erase(server_session) == 1); + } - auto* server_session{&session.GetServerSession()}; + // Close our reference to the server session. + server_session->Close(); +} - // Open a reference to the session to ensure it is not closes while the service request - // completes asynchronously. - server_session->Open(); +void ServiceThread::Impl::LoopProcess() { + Common::SetCurrentThreadName(m_service_name.c_str()); - requests.emplace([server_session, context{std::move(context)}]() { - // Close the reference. - SCOPE_EXIT({ server_session->Close(); }); + kernel.RegisterHostThread(); - // Complete the service request. - server_session->CompleteSyncRequest(*context); - }); + while (!m_shutdown_requested.load()) { + WaitAndProcessImpl(); } - condition.notify_one(); +} + +void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, + std::shared_ptr<SessionRequestManager> manager) { + // Open the server session. + server_session->Open(); + + { + // Lock to get the set. + std::scoped_lock lk{m_session_mutex}; + + // Insert the session and manager. + m_sessions[server_session] = manager; + } + + // Signal the wakeup event. + m_wakeup_event->Signal(); } ServiceThread::Impl::~Impl() { - condition.notify_all(); - for (auto& thread : threads) { - thread.request_stop(); - thread.join(); + // Shut down the processing thread. + m_shutdown_requested.store(true); + m_wakeup_event->Signal(); + m_thread.join(); + + // Lock mutex. + m_session_mutex.lock(); + + // Close all remaining sessions. + for (const auto& [server_session, manager] : m_sessions) { + server_session->Close(); } + + // Destroy remaining managers. + m_sessions.clear(); + + // Close event. + m_wakeup_event->GetReadableEvent().Close(); + m_wakeup_event->Close(); + + // Close process. + m_process->Close(); +} + +ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) + : kernel{kernel_}, m_service_name{service_name} { + // Initialize process. + m_process = KProcess::Create(kernel); + KProcess::Initialize(m_process, kernel.System(), service_name, + KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); + + // Reserve a new event from the process resource limit + KScopedResourceReservation event_reservation(m_process, LimitableResource::Events); + ASSERT(event_reservation.Succeeded()); + + // Initialize event. + m_wakeup_event = KEvent::Create(kernel); + m_wakeup_event->Initialize(m_process); + + // Commit the event reservation. + event_reservation.Commit(); + + // Register the event. + KEvent::Register(kernel, m_wakeup_event); + + // Start thread. + m_thread = std::jthread([this] { LoopProcess(); }); } -ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} +ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) + : impl{std::make_unique<Impl>(kernel, name)} {} ServiceThread::~ServiceThread() = default; -void ServiceThread::QueueSyncRequest(KSession& session, - std::shared_ptr<HLERequestContext>&& context) { - impl->QueueSyncRequest(session, std::move(context)); +void ServiceThread::RegisterServerSession(KServerSession* session, + std::shared_ptr<SessionRequestManager> manager) { + impl->RegisterServerSession(session, manager); } } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h index c5896f2bd..fb4325531 100644 --- a/src/core/hle/kernel/service_thread.h +++ b/src/core/hle/kernel/service_thread.h @@ -11,13 +11,15 @@ namespace Kernel { class HLERequestContext; class KernelCore; class KSession; +class SessionRequestManager; class ServiceThread final { public: - explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit ServiceThread(KernelCore& kernel, const std::string& name); ~ServiceThread(); - void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); + void RegisterServerSession(KServerSession* session, + std::shared_ptr<SessionRequestManager> manager); private: class Impl; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 319c9f572..ecac97a52 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -24,6 +24,7 @@ #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" @@ -382,9 +383,9 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; - R_TRY(port->CreateSession(std::addressof(session), - std::make_shared<SessionRequestManager>(kernel))); - port->Close(); + R_TRY(port->CreateSession(std::addressof(session))); + + kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); // Register the session in the table, close the extra reference. handle_table.Register(*out, session); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5db6588e4..5ab41c0c4 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -99,6 +99,12 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying const auto guard = LockService(); + + if (named_port != nullptr) { + named_port->GetClientPort().Close(); + named_port->GetServerPort().Close(); + named_port = nullptr; + } } void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { @@ -113,15 +119,16 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); - ASSERT(!service_registered); + if (named_port == nullptr) { + ASSERT(!service_registered); - auto* port = Kernel::KPort::Create(kernel); - port->Initialize(max_sessions, false, service_name); - port->GetServerPort().SetSessionHandler(shared_from_this()); + named_port = Kernel::KPort::Create(kernel); + named_port->Initialize(max_sessions, false, service_name); - service_registered = true; + service_registered = true; + } - return port->GetClientPort(); + return named_port->GetClientPort(); } void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { @@ -199,7 +206,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, switch (ctx.GetCommandType()) { case IPC::CommandType::Close: case IPC::CommandType::TIPC_Close: { - session.Close(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); result = IPC::ERR_REMOTE_PROCESS_DEAD; @@ -244,6 +250,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); + system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler); Account::InstallInterfaces(system); AM::InstallInterfaces(*sm, *nv_flinger, system); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index ec9deeee4..22e2119d7 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,6 +20,7 @@ class System; namespace Kernel { class HLERequestContext; class KClientPort; +class KPort; class KServerSession; class ServiceThread; } // namespace Kernel @@ -98,6 +99,9 @@ protected: /// Identifier string used to connect to the service. std::string service_name; + /// Port used by ManageNamedPort. + Kernel::KPort* named_port{}; + private: template <typename T> friend class ServiceFramework; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index cb6c0e96f..84720094f 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6); constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} -ServiceManager::~ServiceManager() = default; + +ServiceManager::~ServiceManager() { + for (auto& [name, port] : service_ports) { + port->GetClientPort().Close(); + port->GetServerPort().Close(); + } +} void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); @@ -43,6 +49,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core return self.sm_interface->CreatePort(); } +void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) { + self.sm_interface->AcceptSession(server_port); +} + Result ServiceManager::RegisterService(std::string name, u32 max_sessions, Kernel::SessionRequestHandlerPtr handler) { @@ -53,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions, return ERR_ALREADY_REGISTERED; } - registered_services.emplace(std::move(name), handler); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, false, name); + + service_ports.emplace(name, port); + registered_services.emplace(name, handler); return ResultSuccess; } @@ -68,24 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) { } registered_services.erase(iter); + service_ports.erase(name); + return ResultSuccess; } ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); - auto it = registered_services.find(name); - if (it == registered_services.end()) { + auto it = service_ports.find(name); + if (it == service_ports.end()) { LOG_ERROR(Service_SM, "Server is not registered! service={}", name); return ERR_SERVICE_NOT_REGISTERED; } - auto* port = Kernel::KPort::Create(kernel); - - port->Initialize(ServerSessionCountMax, false, name); - auto handler = it->second; - port->GetServerPort().SetSessionHandler(std::move(handler)); - - return port; + return it->second; } /** @@ -144,24 +154,20 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); - if (port_result.Failed()) { + auto service = service_manager.GetService<Kernel::SessionRequestHandler>(name); + if (port_result.Failed() || !service) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } auto& port = port_result.Unwrap(); - SCOPE_EXIT({ - port->GetClientPort().Close(); - port->GetServerPort().Close(); - }); // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port->GetClientPort().CreateSession( - std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel)); - result.IsError()) { + if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } + service->AcceptSession(&port->GetServerPort()); LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 878decc6f..02a5dde9e 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -51,6 +51,7 @@ private: class ServiceManager { public: static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); + static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port); explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); @@ -78,6 +79,7 @@ private: /// Map of registered services, retrieved using GetServicePort. std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services; + std::unordered_map<std::string, Kernel::KPort*> service_ports; /// Kernel context Kernel::KernelCore& kernel; diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 46a8439d8..69e0fe808 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -15,10 +15,9 @@ namespace Service::SM { void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { - ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), - "Session is already a domain"); + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain"); LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); - ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); + ctx.GetManager()->ConvertToDomainOnRequestEnd(); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -29,9 +28,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service, "called"); auto& process = *ctx.GetThread().GetOwnerProcess(); - auto& parent_session = *ctx.Session()->GetParent(); - auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); - auto& session_handler = session_manager->SessionHandler(); + auto session_manager = ctx.GetManager(); // FIXME: this is duplicated from the SVC, it should just call it instead // once this is a proper process @@ -46,13 +43,14 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { ASSERT(session != nullptr); // Initialize the session. - session->Initialize(nullptr, parent_session.GetName(), session_manager); + session->Initialize(nullptr, ""); // Commit the session reservation. session_reservation.Commit(); - // Register the session. - session_handler.ClientConnected(&session->GetServerSession()); + // Register with manager. + session_manager->SessionHandler().RegisterSession(&session->GetServerSession(), + session_manager); // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 7d5d37bbc..1e1c42cea 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -11,6 +11,10 @@ #include "core/internal_network/network_interface.h" #include "core/internal_network/socket_proxy.h" +#if YUZU_UNIX +#include <sys/socket.h> +#endif + namespace Network { ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index bcdd60db9..545d69c7e 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -224,6 +224,7 @@ add_library(shader_recompiler STATIC ir_opt/lower_fp16_to_fp32.cpp ir_opt/lower_int64_to_int32.cpp ir_opt/passes.h + ir_opt/position_pass.cpp ir_opt/rescaling_pass.cpp ir_opt/ssa_rewrite_pass.cpp ir_opt/texture_pass.cpp diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index 01f9abc71..3b0176bf6 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp @@ -450,6 +450,9 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I if (program.info.uses_rescaling_uniform) { header += "PARAM scaling[1]={program.local[0..0]};"; } + if (program.info.uses_render_area) { + header += "PARAM render_area[1]={program.local[1..1]};"; + } header += "TEMP "; for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) { header += fmt::format("R{},", index); diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp index 2fc2a0ac6..5bfdecc09 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp @@ -43,6 +43,10 @@ void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) { Alias(inst, value); } +void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) { + Alias(inst, value); +} + void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { Alias(inst, value); } diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index 7e8f37563..0a7d42dda 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -396,6 +396,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { ctx.Add("MOV.F {}.x,scaling[0].z;", inst); } +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) { + ctx.Add("MOV.F {},render_area[0];", inst); +} + void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); } diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index 8b0ac3031..d645fd532 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h @@ -73,6 +73,7 @@ void EmitSampleId(EmitContext& ctx, IR::Inst& inst); void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); void EmitYDirection(EmitContext& ctx, IR::Inst& inst); void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst); void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset); void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value); void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); @@ -195,6 +196,7 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); +void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp index 1be4a0f59..8e5e6cf1f 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp @@ -48,6 +48,10 @@ void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); } +void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { + ctx.AddF32("{}=ftoi({});", inst, value); +} + void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { NotImplemented(); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index fad8d1e30..d7c845469 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -416,6 +416,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { ctx.AddF32("{}=scaling.z;", inst); } +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) { + ctx.AddF32x4("{}=render_area;", inst); +} + void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { ctx.AddU32("{}=lmem[{}];", inst, word_offset); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 639691ba6..96e683b5e 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h @@ -87,6 +87,7 @@ void EmitSampleId(EmitContext& ctx, IR::Inst& inst); void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); void EmitYDirection(EmitContext& ctx, IR::Inst& inst); void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst); void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); @@ -229,6 +230,7 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond, void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index c767a9dc3..5d01ec0cd 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -358,6 +358,9 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile if (info.uses_rescaling_uniform) { header += "layout(location=0) uniform vec4 scaling;"; } + if (info.uses_render_area) { + header += "layout(location=1) uniform vec4 render_area;"; + } DefineConstantBuffers(bindings); DefineConstantBufferIndirect(); DefineStorageBuffers(bindings); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 7567b6fc9..937881484 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h @@ -23,8 +23,12 @@ struct RescalingLayout { alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; u32 down_factor; }; +struct RenderAreaLayout { + std::array<f32, 4> render_area; +}; constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); +constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area); [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index c4ca28d11..50daacd95 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp @@ -18,6 +18,10 @@ void EmitBitCastU64F64(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } +void EmitBitCastS32F32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); +} + void EmitBitCastF16U16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } 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 2c68aba39..a4751b42d 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -353,7 +353,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { case IR::Attribute::TessellationEvaluationPointV: return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); - default: throw NotImplementedException("Read attribute {}", attr); } @@ -537,6 +536,17 @@ Id EmitResolutionDownFactor(EmitContext& ctx) { } } +Id EmitRenderArea(EmitContext& ctx) { + if (ctx.profile.unified_descriptor_binding) { + const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[4])}; + const Id index{ctx.Const(ctx.render_are_member_index)}; + const Id pointer{ctx.OpAccessChain(pointer_type, ctx.render_area_push_constant, index)}; + return ctx.OpLoad(ctx.F32[4], pointer); + } else { + throw NotImplementedException("SPIR-V Instruction"); + } +} + Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; return ctx.OpLoad(ctx.U32[1], pointer); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 984d072b4..7070c8fda 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -76,6 +76,7 @@ Id EmitSampleId(EmitContext& ctx); Id EmitIsHelperInvocation(EmitContext& ctx); Id EmitYDirection(EmitContext& ctx); Id EmitResolutionDownFactor(EmitContext& ctx); +Id EmitRenderArea(EmitContext& ctx); Id EmitLoadLocal(EmitContext& ctx, Id word_offset); void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); Id EmitUndefU1(EmitContext& ctx); @@ -177,7 +178,8 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value); void EmitBitCastU16F16(EmitContext& ctx); Id EmitBitCastU32F32(EmitContext& ctx, Id value); void EmitBitCastU64F64(EmitContext& ctx); -void EmitBitCastF16U16(EmitContext& ctx); +void EmitBitCastS32F32(EmitContext& ctx); +void EmitBitCastF16U16(EmitContext&); Id EmitBitCastF32U32(EmitContext& ctx, Id value); void EmitBitCastF64U64(EmitContext& ctx); Id EmitPackUint2x32(EmitContext& ctx, Id value); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index aecc4c612..c26ad8f93 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -473,6 +473,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf DefineAttributeMemAccess(program.info); DefineGlobalMemoryFunctions(program.info); DefineRescalingInput(program.info); + DefineRenderArea(program.info); } EmitContext::~EmitContext() = default; @@ -982,6 +983,36 @@ void EmitContext::DefineRescalingInputUniformConstant() { } } +void EmitContext::DefineRenderArea(const Info& info) { + if (!info.uses_render_area) { + return; + } + + if (profile.unified_descriptor_binding) { + boost::container::static_vector<Id, 1> members{}; + u32 member_index{0}; + + members.push_back(F32[4]); + render_are_member_index = member_index++; + + const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))}; + Decorate(push_constant_struct, spv::Decoration::Block); + Name(push_constant_struct, "RenderAreaInfo"); + + MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0); + MemberName(push_constant_struct, render_are_member_index, "render_area"); + + const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)}; + render_area_push_constant = + AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant); + Name(render_area_push_constant, "render_area_push_constants"); + + if (profile.supported_spirv >= 0x00010400) { + interfaces.push_back(render_area_push_constant); + } + } +} + void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { if (info.constant_buffer_descriptors.empty()) { return; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index bc25b8b84..c86e50911 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -243,6 +243,9 @@ public: u32 texture_rescaling_index{}; u32 image_rescaling_index{}; + Id render_area_push_constant{}; + u32 render_are_member_index{}; + Id local_memory{}; Id shared_memory_u8{}; @@ -318,6 +321,7 @@ private: void DefineRescalingInput(const Info& info); void DefineRescalingInputPushConstant(); void DefineRescalingInputUniformConstant(); + void DefineRenderArea(const Info& info); void DefineInputs(const IR::Program& program); void DefineOutputs(const IR::Program& program); diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 9729d48c6..402f2664f 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -22,6 +22,10 @@ public: [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; + [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0; + + [[nodiscard]] virtual u32 ReadViewportTransformState() = 0; + [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; [[nodiscard]] virtual u32 LocalMemorySize() const = 0; diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 11086ed8c..d4425f06d 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp @@ -378,6 +378,14 @@ F32 IREmitter::ResolutionDownFactor() { return Inst<F32>(Opcode::ResolutionDownFactor); } +F32 IREmitter::RenderAreaWidth() { + return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 0)); +} + +F32 IREmitter::RenderAreaHeight() { + return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 1)); +} + U32 IREmitter::LaneId() { return Inst<U32>(Opcode::LaneId); } @@ -684,6 +692,11 @@ IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) { } template <> +IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) { + return Inst<IR::S32>(Opcode::BitCastS32F32, value); +} + +template <> IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { return Inst<IR::F32>(Opcode::BitCastF32U32, value); } diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 25839a371..f163c18d9 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -103,6 +103,9 @@ public: [[nodiscard]] F32 ResolutionDownFactor(); + [[nodiscard]] F32 RenderAreaWidth(); + [[nodiscard]] F32 RenderAreaHeight(); + [[nodiscard]] U32 LaneId(); [[nodiscard]] U32 LoadGlobalU8(const U64& address); diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h index 752879a18..e70d7745c 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.h +++ b/src/shader_recompiler/frontend/ir/opcodes.h @@ -37,6 +37,7 @@ constexpr Type U8{Type::U8}; constexpr Type U16{Type::U16}; constexpr Type U32{Type::U32}; constexpr Type U64{Type::U64}; +constexpr Type S32{Type::S32}; constexpr Type F16{Type::F16}; constexpr Type F32{Type::F32}; constexpr Type F64{Type::F64}; diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 86410ddfc..88aa077ee 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc @@ -63,6 +63,7 @@ OPCODE(SampleId, U32, OPCODE(IsHelperInvocation, U1, ) OPCODE(YDirection, F32, ) OPCODE(ResolutionDownFactor, F32, ) +OPCODE(RenderArea, F32x4, ) // Undefined OPCODE(UndefU1, U1, ) @@ -173,6 +174,7 @@ OPCODE(SelectF64, F64, U1, OPCODE(BitCastU16F16, U16, F16, ) OPCODE(BitCastU32F32, U32, F32, ) OPCODE(BitCastU64F64, U64, F64, ) +OPCODE(BitCastS32F32, S32, F32, ) OPCODE(BitCastF16U16, F16, U16, ) OPCODE(BitCastF32U32, F32, U32, ) OPCODE(BitCastF64U64, F64, U64, ) diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h index 04c8c4ddb..5a7c706ad 100644 --- a/src/shader_recompiler/frontend/ir/type.h +++ b/src/shader_recompiler/frontend/ir/type.h @@ -24,21 +24,22 @@ enum class Type { U16 = 1 << 7, U32 = 1 << 8, U64 = 1 << 9, - F16 = 1 << 10, - F32 = 1 << 11, - F64 = 1 << 12, - U32x2 = 1 << 13, - U32x3 = 1 << 14, - U32x4 = 1 << 15, - F16x2 = 1 << 16, - F16x3 = 1 << 17, - F16x4 = 1 << 18, - F32x2 = 1 << 19, - F32x3 = 1 << 20, - F32x4 = 1 << 21, - F64x2 = 1 << 22, - F64x3 = 1 << 23, - F64x4 = 1 << 24, + S32 = 1 << 10, + F16 = 1 << 11, + F32 = 1 << 12, + F64 = 1 << 13, + U32x2 = 1 << 14, + U32x3 = 1 << 15, + U32x4 = 1 << 16, + F16x2 = 1 << 17, + F16x3 = 1 << 18, + F16x4 = 1 << 19, + F32x2 = 1 << 20, + F32x3 = 1 << 21, + F32x4 = 1 << 22, + F64x2 = 1 << 23, + F64x3 = 1 << 24, + F64x4 = 1 << 25, }; DECLARE_ENUM_FLAG_OPERATORS(Type) diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp index 346169328..30ba12316 100644 --- a/src/shader_recompiler/frontend/ir/value.cpp +++ b/src/shader_recompiler/frontend/ir/value.cpp @@ -23,6 +23,8 @@ Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {} Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} +Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {} + Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} @@ -69,6 +71,7 @@ bool Value::operator==(const Value& other) const { return imm_u16 == other.imm_u16; case Type::U32: case Type::F32: + case Type::S32: return imm_u32 == other.imm_u32; case Type::U64: case Type::F64: diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 6a673ca05..e8bbb93a5 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -44,6 +44,7 @@ public: explicit Value(u8 value) noexcept; explicit Value(u16 value) noexcept; explicit Value(u32 value) noexcept; + explicit Value(s32 value) noexcept; explicit Value(f32 value) noexcept; explicit Value(u64 value) noexcept; explicit Value(f64 value) noexcept; @@ -66,6 +67,7 @@ public: [[nodiscard]] u8 U8() const; [[nodiscard]] u16 U16() const; [[nodiscard]] u32 U32() const; + [[nodiscard]] s32 S32() const; [[nodiscard]] f32 F32() const; [[nodiscard]] u64 U64() const; [[nodiscard]] f64 F64() const; @@ -85,6 +87,7 @@ private: u8 imm_u8; u16 imm_u16; u32 imm_u32; + s32 imm_s32; f32 imm_f32; u64 imm_u64; f64 imm_f64; @@ -266,6 +269,7 @@ using U8 = TypedValue<Type::U8>; using U16 = TypedValue<Type::U16>; using U32 = TypedValue<Type::U32>; using U64 = TypedValue<Type::U64>; +using S32 = TypedValue<Type::S32>; using F16 = TypedValue<Type::F16>; using F32 = TypedValue<Type::F32>; using F64 = TypedValue<Type::F64>; @@ -377,6 +381,14 @@ inline u32 Value::U32() const { return imm_u32; } +inline s32 Value::S32() const { + if (IsIdentity()) { + return inst->Arg(0).S32(); + } + DEBUG_ASSERT(type == Type::S32); + return imm_s32; +} + inline f32 Value::F32() const { if (IsIdentity()) { return inst->Arg(0).F32(); diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index b58741d4d..b7162f719 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -220,6 +220,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo Optimization::ConstantPropagationPass(program); + Optimization::PositionPass(env, program); + Optimization::GlobalMemoryToStorageBufferPass(program); Optimization::TexturePass(env, program); diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 6ff8e4266..24f609d69 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -17,6 +17,7 @@ void LowerFp16ToFp32(IR::Program& program); void LowerInt64ToInt32(IR::Program& program); void RescalingPass(IR::Program& program); void SsaRewritePass(IR::Program& program); +void PositionPass(Environment& env, IR::Program& program); void TexturePass(Environment& env, IR::Program& program); void VerificationPass(const IR::Program& program); diff --git a/src/shader_recompiler/ir_opt/position_pass.cpp b/src/shader_recompiler/ir_opt/position_pass.cpp new file mode 100644 index 000000000..3c20b7189 --- /dev/null +++ b/src/shader_recompiler/ir_opt/position_pass.cpp @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <boost/container/small_vector.hpp> + +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/ir_opt/passes.h" + +namespace Shader::Optimization { + +namespace { +struct PositionInst { + IR::Inst* inst; + IR::Block* block; + IR::Attribute attr; +}; +using PositionInstVector = boost::container::small_vector<PositionInst, 24>; +} // Anonymous namespace + +void PositionPass(Environment& env, IR::Program& program) { + if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) { + return; + } + + Info& info{program.info}; + info.uses_render_area = true; + + PositionInstVector to_replace; + for (IR::Block* const block : program.post_order_blocks) { + for (IR::Inst& inst : block->Instructions()) { + switch (inst.GetOpcode()) { + case IR::Opcode::SetAttribute: { + const IR::Attribute attr{inst.Arg(0).Attribute()}; + switch (attr) { + case IR::Attribute::PositionX: + case IR::Attribute::PositionY: { + to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr}); + break; + } + default: + break; + } + break; + } + default: + break; + } + } + } + + for (PositionInst& position_inst : to_replace) { + IR::IREmitter ir{*position_inst.block, + IR::Block::InstructionList::s_iterator_to(*position_inst.inst)}; + const IR::F32 value(position_inst.inst->Arg(1)); + const IR::F32F64 scale(ir.Imm32(2.f)); + const IR::F32 negative_one{ir.Imm32(-1.f)}; + switch (position_inst.attr) { + case IR::Attribute::PositionX: { + position_inst.inst->SetArg( + 1, + ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one)); + break; + } + case IR::Attribute::PositionY: { + position_inst.inst->SetArg( + 1, + ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one)); + break; + } + default: + break; + } + } +} +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index e8be58357..9eff84a3d 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -7,6 +7,7 @@ #include <boost/container/small_vector.hpp> +#include "common/settings.h" #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/breadth_first_search.h" @@ -363,6 +364,14 @@ TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { return env.ReadTextureType(lhs_raw | rhs_raw); } +TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { + const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; + const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; + const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)}; + const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)}; + return env.ReadTexturePixelFormat(lhs_raw | rhs_raw); +} + class Descriptors { public: explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, @@ -451,6 +460,38 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)), ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); } + +void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { + const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + auto get_max_value = [pixel_format]() -> float { + switch (pixel_format) { + case TexturePixelFormat::A8B8G8R8_SNORM: + case TexturePixelFormat::R8G8_SNORM: + case TexturePixelFormat::R8_SNORM: + return 1.f / std::numeric_limits<char>::max(); + case TexturePixelFormat::R16G16B16A16_SNORM: + case TexturePixelFormat::R16G16_SNORM: + case TexturePixelFormat::R16_SNORM: + return 1.f / std::numeric_limits<short>::max(); + default: + throw InvalidArgument("Invalid texture pixel format"); + } + }; + + const IR::Value new_inst{&*block.PrependNewInst(it, inst)}; + const IR::F32 x(ir.CompositeExtract(new_inst, 0)); + const IR::F32 y(ir.CompositeExtract(new_inst, 1)); + const IR::F32 z(ir.CompositeExtract(new_inst, 2)); + const IR::F32 w(ir.CompositeExtract(new_inst, 3)); + const IR::F16F32F64 max_value(ir.Imm32(get_max_value())); + const IR::Value converted = + ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value), + ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value), + ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value), + ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value)); + inst.ReplaceUsesWith(converted); +} } // Anonymous namespace void TexturePass(Environment& env, IR::Program& program) { @@ -597,6 +638,14 @@ void TexturePass(Environment& env, IR::Program& program) { } else { inst->SetArg(0, IR::Value{}); } + + if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && + inst->GetOpcode() == IR::Opcode::ImageFetch && flags.type == TextureType::Buffer) { + const auto pixel_format = ReadTexturePixelFormat(env, cbuf); + if (pixel_format != TexturePixelFormat::OTHER) { + PathTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); + } + } } } diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index 81097bf1a..f31e1f821 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -29,6 +29,16 @@ enum class TextureType : u32 { }; constexpr u32 NUM_TEXTURE_TYPES = 9; +enum class TexturePixelFormat : u32 { + A8B8G8R8_SNORM, + R8_SNORM, + R8G8_SNORM, + R16G16B16A16_SNORM, + R16G16_SNORM, + R16_SNORM, + OTHER +}; + enum class ImageFormat : u32 { Typeless, R8_UINT, @@ -182,6 +192,7 @@ struct Info { bool uses_shadow_lod{}; bool uses_rescaling_uniform{}; bool uses_cbuf_indirect{}; + bool uses_render_area{}; IR::Type used_constant_buffer_types{}; IR::Type used_storage_buffer_types{}; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 08f4d69ab..6af4ae793 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -29,17 +29,17 @@ constexpr std::array PROGRAM_LUT{ [[nodiscard]] GLenum GetTextureBufferFormat(GLenum gl_format) { switch (gl_format) { case GL_RGBA8_SNORM: - return GL_RGBA8; + return GL_RGBA8I; case GL_R8_SNORM: - return GL_R8; + return GL_R8I; case GL_RGBA16_SNORM: - return GL_RGBA16; + return GL_RGBA16I; case GL_R16_SNORM: - return GL_R16; + return GL_R16I; case GL_RG16_SNORM: - return GL_RG16; + return GL_RG16I; case GL_RG8_SNORM: - return GL_RG8; + return GL_RG8I; default: return gl_format; } @@ -96,9 +96,6 @@ GLuint Buffer::View(u32 offset, u32 size, PixelFormat format) { texture.Create(GL_TEXTURE_BUFFER); const GLenum gl_format{MaxwellToGL::GetFormatTuple(format).internal_format}; const GLenum texture_format{GetTextureBufferFormat(gl_format)}; - if (texture_format != gl_format) { - LOG_WARNING(Render_OpenGL, "Emulating SNORM texture buffer with UNORM."); - } glTextureBufferRange(texture.handle, texture_format, buffer.handle, offset, size); views.push_back({ .offset = offset, diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 1d20a79ec..c115dabe1 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -503,6 +503,17 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { float_image_scaling_mask, down_factor, 0.0f); } } + if (info.uses_render_area) { + const auto render_area_width(static_cast<GLfloat>(regs.surface_clip.width)); + const auto render_area_height(static_cast<GLfloat>(regs.surface_clip.height)); + if (use_assembly) { + glProgramLocalParameter4fARB(AssemblyStage(stage), 1, render_area_width, + render_area_height, 0.0f, 0.0f); + } else { + glProgramUniform4f(source_programs[stage].handle, 1, render_area_width, + render_area_height, 0.0f, 0.0f); + } + } }}; if constexpr (Spec::enabled_stages[0]) { prepare_stage(0); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1590b21de..72e314d39 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -618,6 +618,16 @@ void RasterizerOpenGL::SyncViewport() { } flags[Dirty::Viewport0 + index] = false; + if (!regs.viewport_scale_offset_enbled) { + const auto x = static_cast<GLfloat>(regs.surface_clip.x); + const auto y = static_cast<GLfloat>(regs.surface_clip.y); + const auto width = static_cast<GLfloat>(regs.surface_clip.width); + const auto height = static_cast<GLfloat>(regs.surface_clip.height); + glViewportIndexedf(static_cast<GLuint>(index), x, y, width != 0.0f ? width : 1.0f, + height != 0.0f ? height : 1.0f); + continue; + } + const auto& src = regs.viewport_transform[index]; GLfloat x = conv(src.translate_x - src.scale_x); GLfloat y = conv(src.translate_y - src.scale_y); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index e94cfdb1a..977709518 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -49,7 +49,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 6; +constexpr u32 CACHE_VERSION = 7; template <typename Container> auto MakeSpan(Container& container) { diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index b24f3424a..b7843e995 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -68,13 +68,15 @@ public: } vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const { + using Shader::Backend::SPIRV::RenderAreaLayout; using Shader::Backend::SPIRV::RescalingLayout; const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u; const VkPushConstantRange range{ .stageFlags = static_cast<VkShaderStageFlags>( is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS), .offset = 0, - .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset, + .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset + + static_cast<u32>(sizeof(RenderAreaLayout)), }; return device->GetLogical().CreatePipelineLayout({ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -167,6 +169,12 @@ private: u32 image_bit{1u}; }; +class RenderAreaPushConstant { +public: + bool uses_render_area{}; + std::array<f32, 4> words{}; +}; + inline void PushImageDescriptors(TextureCache& texture_cache, UpdateDescriptorQueue& update_descriptor_queue, const Shader::Info& info, RescalingPushConstant& rescaling, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index c3f66c8a3..1aa116cea 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -31,6 +31,7 @@ namespace { using boost::container::small_vector; using boost::container::static_vector; using Shader::ImageBufferDescriptor; +using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET; using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET; using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; @@ -433,12 +434,19 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { update_descriptor_queue.Acquire(); RescalingPushConstant rescaling; + RenderAreaPushConstant render_area; const VkSampler* samplers_it{samplers.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()}; const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { buffer_cache.BindHostStageBuffers(stage); PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling, samplers_it, views_it); + const auto& info{stage_infos[0]}; + if (info.uses_render_area) { + render_area.uses_render_area = true; + render_area.words = {static_cast<float>(regs.surface_clip.width), + static_cast<float>(regs.surface_clip.height)}; + } }}; if constexpr (Spec::enabled_stages[0]) { prepare_stage(0); @@ -455,10 +463,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if constexpr (Spec::enabled_stages[4]) { prepare_stage(4); } - ConfigureDraw(rescaling); + ConfigureDraw(rescaling, render_area); } -void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { +void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, + const RenderAreaPushConstant& render_area) { texture_cache.UpdateRenderTargets(false); scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); @@ -474,7 +483,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; const void* const descriptor_data{update_descriptor_queue.UpdateData()}; scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), - is_rescaling, update_rescaling](vk::CommandBuffer cmdbuf) { + is_rescaling, update_rescaling, + uses_render_area = render_area.uses_render_area, + render_area_data = render_area.words](vk::CommandBuffer cmdbuf) { if (bind_pipeline) { cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); } @@ -488,6 +499,11 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor), &scale_down_factor); } + if (uses_render_area) { + cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS, + RENDERAREA_LAYOUT_OFFSET, sizeof(render_area_data), + &render_area_data); + } if (!descriptor_set_layout) { return; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 85602592b..6bf577d25 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -62,6 +62,7 @@ class Device; class PipelineStatistics; class RenderPassCache; class RescalingPushConstant; +class RenderAreaPushConstant; class Scheduler; class UpdateDescriptorQueue; @@ -119,7 +120,8 @@ private: template <typename Spec> void ConfigureImpl(bool is_indexed); - void ConfigureDraw(const RescalingPushConstant& rescaling); + void ConfigureDraw(const RescalingPushConstant& rescaling, + const RenderAreaPushConstant& render_are); void MakePipeline(VkRenderPass render_pass); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 13d5a1f67..b42e5be1e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -53,7 +53,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 6; +constexpr u32 CACHE_VERSION = 7; template <typename Container> auto MakeSpan(Container& container) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d94dbf873..f79fa8313 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -683,6 +683,22 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg if (!state_tracker.TouchViewports()) { return; } + if (!regs.viewport_scale_offset_enbled) { + const auto x = static_cast<float>(regs.surface_clip.x); + const auto y = static_cast<float>(regs.surface_clip.y); + const auto width = static_cast<float>(regs.surface_clip.width); + const auto height = static_cast<float>(regs.surface_clip.height); + VkViewport viewport{ + .x = x, + .y = y, + .width = width != 0.0f ? width : 1.0f, + .height = height != 0.0f ? height : 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); }); + return; + } const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; const std::array viewports{ diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index fbabb3219..37bb76b72 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -19,6 +19,7 @@ #include "video_core/engines/kepler_compute.h" #include "video_core/memory_manager.h" #include "video_core/shader_environment.h" +#include "video_core/texture_cache/format_lookup_table.h" #include "video_core/textures/texture.h" namespace VideoCommon { @@ -33,7 +34,7 @@ static u64 MakeCbufKey(u32 index, u32 offset) { return (static_cast<u64>(index) << 32) | offset; } -static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { +static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& entry) { switch (entry.texture_type) { case Tegra::Texture::TextureType::Texture1D: return Shader::TextureType::Color1D; @@ -59,6 +60,26 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { } } +static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) { + switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type, + entry.a_type, entry.srgb_conversion)) { + case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM: + return Shader::TexturePixelFormat::A8B8G8R8_SNORM; + case VideoCore::Surface::PixelFormat::R8_SNORM: + return Shader::TexturePixelFormat::R8_SNORM; + case VideoCore::Surface::PixelFormat::R8G8_SNORM: + return Shader::TexturePixelFormat::R8G8_SNORM; + case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM: + return Shader::TexturePixelFormat::R16G16B16A16_SNORM; + case VideoCore::Surface::PixelFormat::R16G16_SNORM: + return Shader::TexturePixelFormat::R16G16_SNORM; + case VideoCore::Surface::PixelFormat::R16_SNORM: + return Shader::TexturePixelFormat::R16_SNORM; + default: + return Shader::TexturePixelFormat::OTHER; + } +} + static std::string_view StageToPrefix(Shader::Stage stage) { switch (stage) { case Shader::Stage::VertexB: @@ -178,22 +199,31 @@ void GenericEnvironment::Dump(u64 hash) { void GenericEnvironment::Serialize(std::ofstream& file) const { const u64 code_size{static_cast<u64>(CachedSize())}; const u64 num_texture_types{static_cast<u64>(texture_types.size())}; + const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) + .write(reinterpret_cast<const char*>(&num_texture_pixel_formats), + sizeof(num_texture_pixel_formats)) .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest)) .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest)) + .write(reinterpret_cast<const char*>(&viewport_transform_state), + sizeof(viewport_transform_state)) .write(reinterpret_cast<const char*>(&stage), sizeof(stage)) .write(reinterpret_cast<const char*>(code.data()), code_size); for (const auto& [key, type] : texture_types) { file.write(reinterpret_cast<const char*>(&key), sizeof(key)) .write(reinterpret_cast<const char*>(&type), sizeof(type)); } + for (const auto& [key, format] : texture_pixel_formats) { + file.write(reinterpret_cast<const char*>(&key), sizeof(key)) + .write(reinterpret_cast<const char*>(&format), sizeof(format)); + } for (const auto& [key, type] : cbuf_values) { file.write(reinterpret_cast<const char*>(&key), sizeof(key)) .write(reinterpret_cast<const char*>(&type), sizeof(type)); @@ -237,15 +267,13 @@ std::optional<u64> GenericEnvironment::TryFindSize() { return std::nullopt; } -Shader::TextureType GenericEnvironment::ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, - bool via_header_index, u32 raw) { +Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit, + bool via_header_index, u32 raw) { const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)}; const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)}; Tegra::Texture::TICEntry entry; gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry)); - const Shader::TextureType result{ConvertType(entry)}; - texture_types.emplace(raw, result); - return result; + return entry; } GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, @@ -305,8 +333,27 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { const auto& regs{maxwell3d->regs}; const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; - return ReadTextureTypeImpl(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, - handle); + auto entry = + ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); + const Shader::TextureType result{ConvertTextureType(entry)}; + texture_types.emplace(handle, result); + return result; +} + +Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handle) { + const auto& regs{maxwell3d->regs}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; + auto entry = + ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); + const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry)); + texture_pixel_formats.emplace(handle, result); + return result; +} + +u32 GraphicsEnvironment::ReadViewportTransformState() { + const auto& regs{maxwell3d->regs}; + viewport_transform_state = regs.viewport_scale_offset_enbled; + return viewport_transform_state; } ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, @@ -337,21 +384,41 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { const auto& regs{kepler_compute->regs}; const auto& qmd{kepler_compute->launch_description}; - return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + const Shader::TextureType result{ConvertTextureType(entry)}; + texture_types.emplace(handle, result); + return result; +} + +Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle) { + const auto& regs{kepler_compute->regs}; + const auto& qmd{kepler_compute->launch_description}; + auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry)); + texture_pixel_formats.emplace(handle, result); + return result; +} + +u32 ComputeEnvironment::ReadViewportTransformState() { + return viewport_transform_state; } void FileEnvironment::Deserialize(std::ifstream& file) { u64 code_size{}; u64 num_texture_types{}; + u64 num_texture_pixel_formats{}; u64 num_cbuf_values{}; file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) + .read(reinterpret_cast<char*>(&num_texture_pixel_formats), + sizeof(num_texture_pixel_formats)) .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest)) .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest)) + .read(reinterpret_cast<char*>(&viewport_transform_state), sizeof(viewport_transform_state)) .read(reinterpret_cast<char*>(&stage), sizeof(stage)); code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64))); file.read(reinterpret_cast<char*>(code.get()), code_size); @@ -362,6 +429,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) { .read(reinterpret_cast<char*>(&type), sizeof(type)); texture_types.emplace(key, type); } + for (size_t i = 0; i < num_texture_pixel_formats; ++i) { + u32 key; + Shader::TexturePixelFormat format; + file.read(reinterpret_cast<char*>(&key), sizeof(key)) + .read(reinterpret_cast<char*>(&format), sizeof(format)); + texture_pixel_formats.emplace(key, format); + } for (size_t i = 0; i < num_cbuf_values; ++i) { u64 key; u32 value; @@ -409,6 +483,18 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) { return it->second; } +Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) { + const auto it{texture_pixel_formats.find(handle)}; + if (it == texture_pixel_formats.end()) { + throw Shader::LogicError("Uncached read texture pixel format"); + } + return it->second; +} + +u32 FileEnvironment::ReadViewportTransformState() { + return viewport_transform_state; +} + u32 FileEnvironment::LocalMemorySize() const { return local_memory_size; } diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 8b3b8e9f5..bb55b029f 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -63,14 +63,15 @@ public: protected: std::optional<u64> TryFindSize(); - Shader::TextureType ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, bool via_header_index, - u32 raw); + Tegra::Texture::TICEntry ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit, + bool via_header_index, u32 raw); Tegra::MemoryManager* gpu_memory{}; GPUVAddr program_base{}; std::vector<u64> code; std::unordered_map<u32, Shader::TextureType> texture_types; + std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u64, u32> cbuf_values; u32 local_memory_size{}; @@ -85,6 +86,8 @@ protected: u32 cached_highest = 0; u32 initial_offset = 0; + u32 viewport_transform_state = 1; + bool has_unbound_instructions = false; }; @@ -102,6 +105,10 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; + + u32 ReadViewportTransformState() override; + private: Tegra::Engines::Maxwell3D* maxwell3d{}; size_t stage_index{}; @@ -120,6 +127,10 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; + + u32 ReadViewportTransformState() override; + private: Tegra::Engines::KeplerCompute* kepler_compute{}; }; @@ -143,6 +154,10 @@ public: [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; + [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; + + [[nodiscard]] u32 ReadViewportTransformState() override; + [[nodiscard]] u32 LocalMemorySize() const override; [[nodiscard]] u32 SharedMemorySize() const override; @@ -156,6 +171,7 @@ public: private: std::unique_ptr<u64[]> code; std::unordered_map<u32, Shader::TextureType> texture_types; + std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u64, u32> cbuf_values; std::array<u32, 3> workgroup_size{}; u32 local_memory_size{}; @@ -164,6 +180,7 @@ private: u32 read_lowest{}; u32 read_highest{}; u32 initial_offset{}; + u32 viewport_transform_state = 1; }; void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 1223df5a0..e8c908b42 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -516,7 +516,6 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block; - UNIMPLEMENTED_IF(info.tile_width_spacing > 0); UNIMPLEMENTED_IF(copy.image_offset.x != 0); UNIMPLEMENTED_IF(copy.image_offset.y != 0); UNIMPLEMENTED_IF(copy.image_offset.z != 0); diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 29d506c47..239f12382 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -315,7 +315,7 @@ target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) if (NOT WIN32) target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if (UNIX AND NOT APPLE) target_link_libraries(yuzu PRIVATE Qt::DBus) endif() diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 927dd1069..343f3b8e5 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -820,6 +820,8 @@ void Config::ReadUIGamelistValues() { ReadBasicSetting(UISettings::values.show_add_ons); ReadBasicSetting(UISettings::values.show_compat); + ReadBasicSetting(UISettings::values.show_size); + ReadBasicSetting(UISettings::values.show_types); ReadBasicSetting(UISettings::values.game_icon_size); ReadBasicSetting(UISettings::values.folder_icon_size); ReadBasicSetting(UISettings::values.row_1_text_id); @@ -1416,6 +1418,8 @@ void Config::SaveUIGamelistValues() { WriteBasicSetting(UISettings::values.show_add_ons); WriteBasicSetting(UISettings::values.show_compat); + WriteBasicSetting(UISettings::values.show_size); + WriteBasicSetting(UISettings::values.show_types); WriteBasicSetting(UISettings::values.game_icon_size); WriteBasicSetting(UISettings::values.folder_icon_size); WriteBasicSetting(UISettings::values.row_1_text_id); diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 92e6da6ee..2ebb80302 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -73,6 +73,8 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent) // Force game list reload if any of the relevant settings are changed. connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); + connect(ui->show_size, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); + connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ConfigureUi::RequestGameListUpdate); connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), @@ -111,6 +113,8 @@ void ConfigureUi::ApplyConfiguration() { ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); UISettings::values.show_compat = ui->show_compat->isChecked(); + UISettings::values.show_size = ui->show_size->isChecked(); + UISettings::values.show_types = ui->show_types->isChecked(); UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); @@ -132,6 +136,8 @@ void ConfigureUi::SetConfiguration() { ui->language_combobox->findData(UISettings::values.language)); ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); + ui->show_size->setChecked(UISettings::values.show_size.GetValue()); + ui->show_types->setChecked(UISettings::values.show_types.GetValue()); ui->game_icon_size_combobox->setCurrentIndex( ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); ui->folder_icon_size_combobox->setCurrentIndex( diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index f0b719ba3..10bb27312 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>363</width> - <height>507</height> + <height>562</height> </rect> </property> <property name="windowTitle"> @@ -91,6 +91,20 @@ </widget> </item> <item> + <widget class="QCheckBox" name="show_size"> + <property name="text"> + <string>Show Size Column</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="show_types"> + <property name="text"> + <string>Show File Types Column</string> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2"> <item> <widget class="QLabel" name="game_icon_size_label"> diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index d6adfca16..5c33c1b0f 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -788,6 +788,8 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { // Update the columns in case UISettings has changed tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); + tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types); + tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); // Delete any rows that might already exist if we're repopulating item_model->removeRows(0, item_model->rowCount()); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7b16d7f7e..59e56633a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -9,7 +9,7 @@ #ifdef __APPLE__ #include <unistd.h> // for chdir #endif -#ifdef __linux__ +#ifdef __unix__ #include <csignal> #include <sys/socket.h> #endif @@ -275,7 +275,7 @@ static void OverrideWindowsFont() { #endif bool GMainWindow::CheckDarkMode() { -#ifdef __linux__ +#ifdef __unix__ const QPalette test_palette(qApp->palette()); const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text); const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); @@ -283,7 +283,7 @@ bool GMainWindow::CheckDarkMode() { #else // TODO: Windows return false; -#endif // __linux__ +#endif // __unix__ } GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) @@ -291,7 +291,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, provider{std::make_unique<FileSys::ManualContentProvider>()} { -#ifdef __linux__ +#ifdef __unix__ SetupSigInterrupts(); #endif system->Initialize(); @@ -509,7 +509,7 @@ GMainWindow::~GMainWindow() { delete render_window; } -#ifdef __linux__ +#ifdef __unix__ ::close(sig_interrupt_fds[0]); ::close(sig_interrupt_fds[1]); #endif @@ -1379,7 +1379,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } void GMainWindow::SetupPrepareForSleep() { -#ifdef __linux__ +#ifdef __unix__ auto bus = QDBusConnection::systemBus(); if (bus.isConnected()) { const bool success = bus.connect( @@ -1393,7 +1393,7 @@ void GMainWindow::SetupPrepareForSleep() { } else { LOG_WARNING(Frontend, "QDBusConnection system bus is not connected"); } -#endif // __linux__ +#endif // __unix__ } void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { @@ -1415,7 +1415,7 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { } } -#ifdef __linux__ +#ifdef __unix__ static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) { if (!QDBusConnection::sessionBus().isConnected()) { return {}; @@ -1500,14 +1500,14 @@ void GMainWindow::OnSigInterruptNotifierActivated() { emit SigInterrupt(); } -#endif // __linux__ +#endif // __unix__ void GMainWindow::PreventOSSleep() { #ifdef _WIN32 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); #elif defined(HAVE_SDL2) SDL_DisableScreenSaver(); -#ifdef __linux__ +#ifdef __unix__ auto reply = HoldWakeLockLinux(winId()); if (reply) { wake_lock = std::move(reply.value()); @@ -1521,7 +1521,7 @@ void GMainWindow::AllowOSSleep() { SetThreadExecutionState(ES_CONTINUOUS); #elif defined(HAVE_SDL2) SDL_EnableScreenSaver(); -#ifdef __linux__ +#ifdef __unix__ if (!wake_lock.path().isEmpty()) { ReleaseWakeLockLinux(wake_lock); } @@ -4070,7 +4070,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { } void GMainWindow::changeEvent(QEvent* event) { -#ifdef __linux__ +#ifdef __unix__ // PaletteChange event appears to only reach so far into the GUI, explicitly asking to // UpdateUITheme is a decent work around if (event->type() == QEvent::PaletteChange) { @@ -4085,7 +4085,7 @@ void GMainWindow::changeEvent(QEvent* event) { } last_window_color = window_color; } -#endif // __linux__ +#endif // __unix__ QWidget::changeEvent(event); } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f7aa8e417..150ada84c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -15,7 +15,7 @@ #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" -#ifdef __linux__ +#ifdef __unix__ #include <QVariant> #include <QtDBus/QDBusInterface> #include <QtDBus/QtDBus> @@ -255,7 +255,7 @@ private: void changeEvent(QEvent* event) override; void closeEvent(QCloseEvent* event) override; -#ifdef __linux__ +#ifdef __unix__ void SetupSigInterrupts(); static void HandleSigInterrupt(int); void OnSigInterruptNotifierActivated(); @@ -435,7 +435,7 @@ private: // True if TAS recording dialog is visible bool is_tas_recording_dialog_active{}; -#ifdef __linux__ +#ifdef __unix__ QSocketNotifier* sig_interrupt_notifier; static std::array<int, 3> sig_interrupt_fds; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 4f5b2a99d..452038cd9 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -132,6 +132,10 @@ struct Values { // Compatibility List Settings::Setting<bool> show_compat{false, "show_compat"}; + // Size & File Types Column + Settings::Setting<bool> show_size{true, "show_size"}; + Settings::Setting<bool> show_types{true, "show_types"}; + bool configuration_applied; bool reset_to_defaults; Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; |