summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--src/core/hle/ipc_helpers.h17
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp55
-rw-r--r--src/core/hle/kernel/hle_ipc.h29
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_client_port.h3
-rw-r--r--src/core/hle/kernel/k_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.h19
-rw-r--r--src/core/hle/kernel/k_server_session.cpp187
-rw-r--r--src/core/hle/kernel/k_server_session.h41
-rw-r--r--src/core/hle/kernel/k_session.cpp5
-rw-r--r--src/core/hle/kernel/k_session.h3
-rw-r--r--src/core/hle/kernel/kernel.cpp52
-rw-r--r--src/core/hle/kernel/kernel.h15
-rw-r--r--src/core/hle/kernel/service_thread.cpp230
-rw-r--r--src/core/hle/kernel/service_thread.h6
-rw-r--r--src/core/hle/kernel/svc.cpp7
-rw-r--r--src/core/hle/service/service.cpp21
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/sm/sm.cpp44
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp16
-rw-r--r--src/core/internal_network/socket_proxy.cpp4
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp3
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp12
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp31
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h4
-rw-r--r--src/shader_recompiler/environment.h4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp13
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h3
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc2
-rw-r--r--src/shader_recompiler/frontend/ir/type.h31
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp3
-rw-r--r--src/shader_recompiler/frontend/ir/value.h12
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/ir_opt/position_pass.cpp77
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp49
-rw-r--r--src/shader_recompiler/shader_info.h11
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp16
-rw-r--r--src/video_core/shader_environment.cpp104
-rw-r--r--src/video_core/shader_environment.h21
-rw-r--r--src/video_core/texture_cache/util.cpp1
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_ui.cpp6
-rw-r--r--src/yuzu/configuration/configure_ui.ui16
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu/main.cpp26
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/uisettings.h4
72 files changed, 961 insertions, 401 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b625743ea..c6fc5dd9e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -218,11 +218,11 @@ if(ENABLE_QT)
218 set(QT_VERSION 5.15) 218 set(QT_VERSION 5.15)
219 219
220 # Check for system Qt on Linux, fallback to bundled Qt 220 # Check for system Qt on Linux, fallback to bundled Qt
221 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 221 if (UNIX AND NOT APPLE)
222 if (NOT YUZU_USE_BUNDLED_QT) 222 if (NOT YUZU_USE_BUNDLED_QT)
223 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia) 223 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
224 endif() 224 endif()
225 if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) 225 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
226 # Check for dependencies, then enable bundled Qt download 226 # Check for dependencies, then enable bundled Qt download
227 227
228 # Check that the system GLIBCXX version is compatible 228 # Check that the system GLIBCXX version is compatible
@@ -323,7 +323,7 @@ if(ENABLE_QT)
323 323
324 set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH") 324 set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
325 endif() 325 endif()
326 if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT) 326 if (UNIX AND NOT APPLE AND YUZU_USE_BUNDLED_QT)
327 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) 327 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
328 else() 328 else()
329 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) 329 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
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:
86 u32 num_domain_objects{}; 86 u32 num_domain_objects{};
87 const bool always_move_handles{ 87 const bool always_move_handles{
88 (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; 88 (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
89 if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { 89 if (!ctx.GetManager()->IsDomain() || always_move_handles) {
90 num_handles_to_move = num_objects_to_move; 90 num_handles_to_move = num_objects_to_move;
91 } else { 91 } else {
92 num_domain_objects = num_objects_to_move; 92 num_domain_objects = num_objects_to_move;
93 } 93 }
94 94
95 if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { 95 if (ctx.GetManager()->IsDomain()) {
96 raw_data_size += 96 raw_data_size +=
97 static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); 97 static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
98 ctx.write_size += num_domain_objects; 98 ctx.write_size += num_domain_objects;
@@ -125,8 +125,7 @@ public:
125 if (!ctx.IsTipc()) { 125 if (!ctx.IsTipc()) {
126 AlignWithPadding(); 126 AlignWithPadding();
127 127
128 if (ctx.Session()->GetSessionRequestManager()->IsDomain() && 128 if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
129 ctx.HasDomainMessageHeader()) {
130 IPC::DomainMessageHeader domain_header{}; 129 IPC::DomainMessageHeader domain_header{};
131 domain_header.num_objects = num_domain_objects; 130 domain_header.num_objects = num_domain_objects;
132 PushRaw(domain_header); 131 PushRaw(domain_header);
@@ -146,18 +145,18 @@ public:
146 145
147 template <class T> 146 template <class T>
148 void PushIpcInterface(std::shared_ptr<T> iface) { 147 void PushIpcInterface(std::shared_ptr<T> iface) {
149 if (context->Session()->GetSessionRequestManager()->IsDomain()) { 148 if (context->GetManager()->IsDomain()) {
150 context->AddDomainObject(std::move(iface)); 149 context->AddDomainObject(std::move(iface));
151 } else { 150 } else {
152 kernel.CurrentProcess()->GetResourceLimit()->Reserve( 151 kernel.CurrentProcess()->GetResourceLimit()->Reserve(
153 Kernel::LimitableResource::Sessions, 1); 152 Kernel::LimitableResource::Sessions, 1);
154 153
155 auto* session = Kernel::KSession::Create(kernel); 154 auto* session = Kernel::KSession::Create(kernel);
156 session->Initialize(nullptr, iface->GetServiceName(), 155 session->Initialize(nullptr, iface->GetServiceName());
157 std::make_shared<Kernel::SessionRequestManager>(kernel)); 156 iface->RegisterSession(&session->GetServerSession(),
157 std::make_shared<Kernel::SessionRequestManager>(kernel));
158 158
159 context->AddMoveObject(&session->GetClientSession()); 159 context->AddMoveObject(&session->GetClientSession());
160 iface->ClientConnected(&session->GetServerSession());
161 } 160 }
162 } 161 }
163 162
@@ -387,7 +386,7 @@ public:
387 386
388 template <class T> 387 template <class T>
389 std::weak_ptr<T> PopIpcInterface() { 388 std::weak_ptr<T> PopIpcInterface() {
390 ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); 389 ASSERT(context->GetManager()->IsDomain());
391 ASSERT(context->GetDomainMessageHeader().input_object_count > 0); 390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
392 return context->GetDomainHandler<T>(Pop<u32>() - 1); 391 return context->GetDomainHandler<T>(Pop<u32>() - 1);
393 } 392 }
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 @@
16#include "core/hle/kernel/k_auto_object.h" 16#include "core/hle/kernel/k_auto_object.h"
17#include "core/hle/kernel/k_handle_table.h" 17#include "core/hle/kernel/k_handle_table.h"
18#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
19#include "core/hle/kernel/k_server_port.h"
19#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
20#include "core/hle/kernel/k_thread.h" 21#include "core/hle/kernel/k_thread.h"
21#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
@@ -35,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se
35} 36}
36 37
37SessionRequestHandler::~SessionRequestHandler() { 38SessionRequestHandler::~SessionRequestHandler() {
38 kernel.ReleaseServiceThread(service_thread); 39 kernel.ReleaseServiceThread(service_thread.lock());
40}
41
42void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
43 auto* server_session = server_port->AcceptSession();
44 ASSERT(server_session != nullptr);
45
46 RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel));
47}
48
49void SessionRequestHandler::RegisterSession(KServerSession* server_session,
50 std::shared_ptr<SessionRequestManager> manager) {
51 manager->SetSessionHandler(shared_from_this());
52 service_thread.lock()->RegisterServerSession(server_session, manager);
53 server_session->Close();
39} 54}
40 55
41SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} 56SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
@@ -92,7 +107,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
92 } 107 }
93 108
94 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs 109 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
95 context.SetSessionRequestManager(server_session->GetSessionRequestManager()); 110 ASSERT(context.GetManager().get() == this);
96 111
97 // If there is a DomainMessageHeader, then this is CommandType "Request" 112 // If there is a DomainMessageHeader, then this is CommandType "Request"
98 const auto& domain_message_header = context.GetDomainMessageHeader(); 113 const auto& domain_message_header = context.GetDomainMessageHeader();
@@ -130,31 +145,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
130 return ResultSuccess; 145 return ResultSuccess;
131} 146}
132 147
133Result SessionRequestManager::QueueSyncRequest(KSession* parent,
134 std::shared_ptr<HLERequestContext>&& context) {
135 // Ensure we have a session request handler
136 if (this->HasSessionRequestHandler(*context)) {
137 if (auto strong_ptr = this->GetServiceThread().lock()) {
138 strong_ptr->QueueSyncRequest(*parent, std::move(context));
139 } else {
140 ASSERT_MSG(false, "strong_ptr is nullptr!");
141 }
142 } else {
143 ASSERT_MSG(false, "handler is invalid!");
144 }
145
146 return ResultSuccess;
147}
148
149void SessionRequestHandler::ClientConnected(KServerSession* session) {
150 session->GetSessionRequestManager()->SetSessionHandler(shared_from_this());
151
152 // Ensure our server session is tracked globally.
153 kernel.RegisterServerObject(session);
154}
155
156void SessionRequestHandler::ClientDisconnected(KServerSession* session) {}
157
158HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, 148HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
159 KServerSession* server_session_, KThread* thread_) 149 KServerSession* server_session_, KThread* thread_)
160 : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} { 150 : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
@@ -214,7 +204,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
214 // Padding to align to 16 bytes 204 // Padding to align to 16 bytes
215 rp.AlignWithPadding(); 205 rp.AlignWithPadding();
216 206
217 if (Session()->GetSessionRequestManager()->IsDomain() && 207 if (GetManager()->IsDomain() &&
218 ((command_header->type == IPC::CommandType::Request || 208 ((command_header->type == IPC::CommandType::Request ||
219 command_header->type == IPC::CommandType::RequestWithContext) || 209 command_header->type == IPC::CommandType::RequestWithContext) ||
220 !incoming)) { 210 !incoming)) {
@@ -223,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
223 if (incoming || domain_message_header) { 213 if (incoming || domain_message_header) {
224 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); 214 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
225 } else { 215 } else {
226 if (Session()->GetSessionRequestManager()->IsDomain()) { 216 if (GetManager()->IsDomain()) {
227 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); 217 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
228 } 218 }
229 } 219 }
@@ -316,12 +306,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
316 // Write the domain objects to the command buffer, these go after the raw untranslated data. 306 // Write the domain objects to the command buffer, these go after the raw untranslated data.
317 // TODO(Subv): This completely ignores C buffers. 307 // TODO(Subv): This completely ignores C buffers.
318 308
319 if (server_session->GetSessionRequestManager()->IsDomain()) { 309 if (GetManager()->IsDomain()) {
320 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); 310 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
321 for (auto& object : outgoing_domain_objects) { 311 for (auto& object : outgoing_domain_objects) {
322 server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); 312 GetManager()->AppendDomainHandler(std::move(object));
323 cmd_buf[current_offset++] = static_cast<u32_le>( 313 cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
324 server_session->GetSessionRequestManager()->DomainHandlerCount());
325 } 314 }
326 } 315 }
327 316
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;
45class KernelCore; 45class KernelCore;
46class KEvent; 46class KEvent;
47class KHandleTable; 47class KHandleTable;
48class KServerPort;
48class KProcess; 49class KProcess;
49class KServerSession; 50class KServerSession;
50class KThread; 51class KThread;
51class KReadableEvent; 52class KReadableEvent;
52class KSession; 53class KSession;
54class SessionRequestManager;
53class ServiceThread; 55class ServiceThread;
54 56
55enum class ThreadWakeupReason; 57enum class ThreadWakeupReason;
@@ -76,19 +78,9 @@ public:
76 virtual Result HandleSyncRequest(Kernel::KServerSession& session, 78 virtual Result HandleSyncRequest(Kernel::KServerSession& session,
77 Kernel::HLERequestContext& context) = 0; 79 Kernel::HLERequestContext& context) = 0;
78 80
79 /** 81 void AcceptSession(KServerPort* server_port);
80 * Signals that a client has just connected to this HLE handler and keeps the 82 void RegisterSession(KServerSession* server_session,
81 * associated ServerSession alive for the duration of the connection. 83 std::shared_ptr<SessionRequestManager> manager);
82 * @param server_session Owning pointer to the ServerSession associated with the connection.
83 */
84 void ClientConnected(KServerSession* session);
85
86 /**
87 * Signals that a client has just disconnected from this HLE handler and releases the
88 * associated ServerSession.
89 * @param server_session ServerSession associated with the connection.
90 */
91 void ClientDisconnected(KServerSession* session);
92 84
93 std::weak_ptr<ServiceThread> GetServiceThread() const { 85 std::weak_ptr<ServiceThread> GetServiceThread() const {
94 return service_thread; 86 return service_thread;
@@ -170,7 +162,6 @@ public:
170 162
171 Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); 163 Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
172 Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); 164 Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
173 Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context);
174 165
175private: 166private:
176 bool convert_to_domain{}; 167 bool convert_to_domain{};
@@ -350,11 +341,11 @@ public:
350 341
351 template <typename T> 342 template <typename T>
352 std::shared_ptr<T> GetDomainHandler(std::size_t index) const { 343 std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
353 return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); 344 return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
354 } 345 }
355 346
356 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { 347 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
357 manager = std::move(manager_); 348 manager = manager_;
358 } 349 }
359 350
360 std::string Description() const; 351 std::string Description() const;
@@ -363,6 +354,10 @@ public:
363 return *thread; 354 return *thread;
364 } 355 }
365 356
357 std::shared_ptr<SessionRequestManager> GetManager() const {
358 return manager.lock();
359 }
360
366private: 361private:
367 friend class IPC::ResponseBuilder; 362 friend class IPC::ResponseBuilder;
368 363
@@ -396,7 +391,7 @@ private:
396 u32 handles_offset{}; 391 u32 handles_offset{};
397 u32 domain_offset{}; 392 u32 domain_offset{};
398 393
399 std::weak_ptr<SessionRequestManager> manager; 394 std::weak_ptr<SessionRequestManager> manager{};
400 395
401 KernelCore& kernel; 396 KernelCore& kernel;
402 Core::Memory::Memory& memory; 397 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 {
58 return num_sessions < max_sessions; 58 return num_sessions < max_sessions;
59} 59}
60 60
61Result KClientPort::CreateSession(KClientSession** out, 61Result KClientPort::CreateSession(KClientSession** out) {
62 std::shared_ptr<SessionRequestManager> session_manager) {
63 // Reserve a new session from the resource limit. 62 // Reserve a new session from the resource limit.
64 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), 63 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
65 LimitableResource::Sessions); 64 LimitableResource::Sessions);
@@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out,
104 } 103 }
105 104
106 // Initialize the session. 105 // Initialize the session.
107 session->Initialize(this, parent->GetName(), session_manager); 106 session->Initialize(this, parent->GetName());
108 107
109 // Commit the session reservation. 108 // Commit the session reservation.
110 session_reservation.Commit(); 109 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:
52 void Destroy() override; 52 void Destroy() override;
53 bool IsSignaled() const override; 53 bool IsSignaled() const override;
54 54
55 Result CreateSession(KClientSession** out, 55 Result CreateSession(KClientSession** out);
56 std::shared_ptr<SessionRequestManager> session_manager = nullptr);
57 56
58private: 57private:
59 std::atomic<s32> num_sessions{}; 58 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) {
57 57
58 server.EnqueueSession(session); 58 server.EnqueueSession(session);
59 59
60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
61 session_ptr->ClientConnected(server.AcceptSession());
62 } else {
63 ASSERT(false);
64 }
65
66 return ResultSuccess; 60 return ResultSuccess;
67} 61}
68 62
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() {
61 61
62 // Close our reference to our parent. 62 // Close our reference to our parent.
63 parent->Close(); 63 parent->Close();
64
65 // Release host emulation members.
66 session_handler.reset();
67
68 // Ensure that the global list tracking server objects does not hold on to a reference.
69 kernel.UnregisterServerObject(this);
70} 64}
71 65
72bool KServerPort::IsSignaled() const { 66bool 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:
27 27
28 void Initialize(KPort* parent_port_, std::string&& name_); 28 void Initialize(KPort* parent_port_, std::string&& name_);
29 29
30 /// Whether or not this server port has an HLE handler available.
31 bool HasSessionRequestHandler() const {
32 return !session_handler.expired();
33 }
34
35 /// Gets the HLE handler for this port.
36 SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
37 return session_handler;
38 }
39
40 /**
41 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
42 * will inherit a reference to this handler.
43 */
44 void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
45 session_handler = std::move(handler);
46 }
47
48 void EnqueueSession(KServerSession* pending_session); 30 void EnqueueSession(KServerSession* pending_session);
49 31
50 KServerSession* AcceptSession(); 32 KServerSession* AcceptSession();
@@ -65,7 +47,6 @@ private:
65 void CleanupSessions(); 47 void CleanupSessions();
66 48
67 SessionList session_list; 49 SessionList session_list;
68 SessionRequestHandlerWeakPtr session_handler;
69 KPort* parent{}; 50 KPort* parent{};
70}; 51};
71 52
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 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <tuple> 4#include <tuple>
@@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_)
33 33
34KServerSession::~KServerSession() = default; 34KServerSession::~KServerSession() = default;
35 35
36void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, 36void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
37 std::shared_ptr<SessionRequestManager> manager_) {
38 // Set member variables. 37 // Set member variables.
39 parent = parent_session_; 38 parent = parent_session_;
40 name = std::move(name_); 39 name = std::move(name_);
41 manager = manager_;
42} 40}
43 41
44void KServerSession::Destroy() { 42void KServerSession::Destroy() {
@@ -47,18 +45,99 @@ void KServerSession::Destroy() {
47 this->CleanupRequests(); 45 this->CleanupRequests();
48 46
49 parent->Close(); 47 parent->Close();
50
51 // Release host emulation members.
52 manager.reset();
53
54 // Ensure that the global list tracking server objects does not hold on to a reference.
55 kernel.UnregisterServerObject(this);
56} 48}
57 49
58void KServerSession::OnClientClosed() { 50void KServerSession::OnClientClosed() {
59 if (manager && manager->HasSessionHandler()) { 51 KScopedLightLock lk{m_lock};
60 manager->SessionHandler().ClientDisconnected(this); 52
53 // Handle any pending requests.
54 KSessionRequest* prev_request = nullptr;
55 while (true) {
56 // Declare variables for processing the request.
57 KSessionRequest* request = nullptr;
58 KEvent* event = nullptr;
59 KThread* thread = nullptr;
60 bool cur_request = false;
61 bool terminate = false;
62
63 // Get the next request.
64 {
65 KScopedSchedulerLock sl{kernel};
66
67 if (m_current_request != nullptr && m_current_request != prev_request) {
68 // Set the request, open a reference as we process it.
69 request = m_current_request;
70 request->Open();
71 cur_request = true;
72
73 // Get thread and event for the request.
74 thread = request->GetThread();
75 event = request->GetEvent();
76
77 // If the thread is terminating, handle that.
78 if (thread->IsTerminationRequested()) {
79 request->ClearThread();
80 request->ClearEvent();
81 terminate = true;
82 }
83
84 prev_request = request;
85 } else if (!m_request_list.empty()) {
86 // Pop the request from the front of the list.
87 request = std::addressof(m_request_list.front());
88 m_request_list.pop_front();
89
90 // Get thread and event for the request.
91 thread = request->GetThread();
92 event = request->GetEvent();
93 }
94 }
95
96 // If there are no requests, we're done.
97 if (request == nullptr) {
98 break;
99 }
100
101 // All requests must have threads.
102 ASSERT(thread != nullptr);
103
104 // Ensure that we close the request when done.
105 SCOPE_EXIT({ request->Close(); });
106
107 // If we're terminating, close a reference to the thread and event.
108 if (terminate) {
109 thread->Close();
110 if (event != nullptr) {
111 event->Close();
112 }
113 }
114
115 // If we need to, reply.
116 if (event != nullptr && !cur_request) {
117 // There must be no mappings.
118 ASSERT(request->GetSendCount() == 0);
119 ASSERT(request->GetReceiveCount() == 0);
120 ASSERT(request->GetExchangeCount() == 0);
121
122 // // Get the process and page table.
123 // KProcess *client_process = thread->GetOwnerProcess();
124 // auto &client_pt = client_process->GetPageTable();
125
126 // // Reply to the request.
127 // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
128 // ResultSessionClosed);
129
130 // // Unlock the buffer.
131 // // NOTE: Nintendo does not check the result of this.
132 // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
133
134 // Signal the event.
135 event->Signal();
136 }
61 } 137 }
138
139 // Notify.
140 this->NotifyAvailable(ResultSessionClosed);
62} 141}
63 142
64bool KServerSession::IsSignaled() const { 143bool KServerSession::IsSignaled() const {
@@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const {
73 return !m_request_list.empty() && m_current_request == nullptr; 152 return !m_request_list.empty() && m_current_request == nullptr;
74} 153}
75 154
76Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
77 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
78 auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
79
80 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
81
82 return manager->QueueSyncRequest(parent, std::move(context));
83}
84
85Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
86 Result result = manager->CompleteSyncRequest(this, context);
87
88 // The calling thread is waiting for this request to complete, so wake it up.
89 context.GetThread().EndWait(result);
90
91 return result;
92}
93
94Result KServerSession::OnRequest(KSessionRequest* request) { 155Result KServerSession::OnRequest(KSessionRequest* request) {
95 // Create the wait queue. 156 // Create the wait queue.
96 ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; 157 ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
@@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
105 // Check that we're not terminating. 166 // Check that we're not terminating.
106 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); 167 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
107 168
108 if (manager) { 169 // Get whether we're empty.
109 // HLE request. 170 const bool was_empty = m_request_list.empty();
110 auto& memory{kernel.System().Memory()};
111 this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
112 } else {
113 // Non-HLE request.
114
115 // Get whether we're empty.
116 const bool was_empty = m_request_list.empty();
117 171
118 // Add the request to the list. 172 // Add the request to the list.
119 request->Open(); 173 request->Open();
120 m_request_list.push_back(*request); 174 m_request_list.push_back(*request);
121 175
122 // If we were empty, signal. 176 // If we were empty, signal.
123 if (was_empty) { 177 if (was_empty) {
124 this->NotifyAvailable(); 178 this->NotifyAvailable();
125 }
126 } 179 }
127 180
128 // If we have a request event, this is asynchronous, and we don't need to wait. 181 // 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) {
136 return GetCurrentThread(kernel).GetWaitResult(); 189 return GetCurrentThread(kernel).GetWaitResult();
137} 190}
138 191
139Result KServerSession::SendReply() { 192Result KServerSession::SendReply(bool is_hle) {
140 // Lock the session. 193 // Lock the session.
141 KScopedLightLock lk{m_lock}; 194 KScopedLightLock lk{m_lock};
142 195
@@ -171,13 +224,18 @@ Result KServerSession::SendReply() {
171 Result result = ResultSuccess; 224 Result result = ResultSuccess;
172 if (!closed) { 225 if (!closed) {
173 // If we're not closed, send the reply. 226 // If we're not closed, send the reply.
174 Core::Memory::Memory& memory{kernel.System().Memory()}; 227 if (is_hle) {
175 KThread* server_thread{GetCurrentThreadPointer(kernel)}; 228 // HLE servers write directly to a pointer to the thread command buffer. Therefore
176 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 229 // the reply has already been written in this case.
230 } else {
231 Core::Memory::Memory& memory{kernel.System().Memory()};
232 KThread* server_thread{GetCurrentThreadPointer(kernel)};
233 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
177 234
178 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); 235 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
179 auto* dst_msg_buffer = memory.GetPointer(client_message); 236 auto* dst_msg_buffer = memory.GetPointer(client_message);
180 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 237 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
238 }
181 } else { 239 } else {
182 result = ResultSessionClosed; 240 result = ResultSessionClosed;
183 } 241 }
@@ -223,7 +281,8 @@ Result KServerSession::SendReply() {
223 return result; 281 return result;
224} 282}
225 283
226Result KServerSession::ReceiveRequest() { 284Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
285 std::weak_ptr<SessionRequestManager> manager) {
227 // Lock the session. 286 // Lock the session.
228 KScopedLightLock lk{m_lock}; 287 KScopedLightLock lk{m_lock};
229 288
@@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() {
267 326
268 // Receive the message. 327 // Receive the message.
269 Core::Memory::Memory& memory{kernel.System().Memory()}; 328 Core::Memory::Memory& memory{kernel.System().Memory()};
270 KThread* server_thread{GetCurrentThreadPointer(kernel)}; 329 if (out_context != nullptr) {
271 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 330 // HLE request.
331 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
332 *out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
333 (*out_context)->SetSessionRequestManager(manager);
334 (*out_context)
335 ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
336 cmd_buf);
337 } else {
338 KThread* server_thread{GetCurrentThreadPointer(kernel)};
339 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
272 340
273 auto* src_msg_buffer = memory.GetPointer(client_message); 341 auto* src_msg_buffer = memory.GetPointer(client_message);
274 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); 342 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
275 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 343 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
344 }
276 345
277 // We succeeded. 346 // We succeeded.
278 return ResultSuccess; 347 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 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
@@ -16,21 +16,11 @@
16#include "core/hle/kernel/k_synchronization_object.h" 16#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/result.h" 17#include "core/hle/result.h"
18 18
19namespace Core::Memory {
20class Memory;
21}
22
23namespace Core::Timing {
24class CoreTiming;
25struct EventType;
26} // namespace Core::Timing
27
28namespace Kernel { 19namespace Kernel {
29 20
30class HLERequestContext; 21class HLERequestContext;
31class KernelCore; 22class KernelCore;
32class KSession; 23class KSession;
33class SessionRequestHandler;
34class SessionRequestManager; 24class SessionRequestManager;
35class KThread; 25class KThread;
36 26
@@ -46,8 +36,7 @@ public:
46 36
47 void Destroy() override; 37 void Destroy() override;
48 38
49 void Initialize(KSession* parent_session_, std::string&& name_, 39 void Initialize(KSession* parent_session_, std::string&& name_);
50 std::shared_ptr<SessionRequestManager> manager_);
51 40
52 KSession* GetParent() { 41 KSession* GetParent() {
53 return parent; 42 return parent;
@@ -60,32 +49,20 @@ public:
60 bool IsSignaled() const override; 49 bool IsSignaled() const override;
61 void OnClientClosed(); 50 void OnClientClosed();
62 51
63 /// Gets the session request manager, which forwards requests to the underlying service
64 std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() {
65 return manager;
66 }
67
68 /// TODO: flesh these out to match the real kernel 52 /// TODO: flesh these out to match the real kernel
69 Result OnRequest(KSessionRequest* request); 53 Result OnRequest(KSessionRequest* request);
70 Result SendReply(); 54 Result SendReply(bool is_hle = false);
71 Result ReceiveRequest(); 55 Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
56 std::weak_ptr<SessionRequestManager> manager = {});
57
58 Result SendReplyHLE() {
59 return SendReply(true);
60 }
72 61
73private: 62private:
74 /// Frees up waiting client sessions when this server session is about to die 63 /// Frees up waiting client sessions when this server session is about to die
75 void CleanupRequests(); 64 void CleanupRequests();
76 65
77 /// Queues a sync request from the emulated application.
78 Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
79
80 /// Completes a sync request from the emulated application.
81 Result CompleteSyncRequest(HLERequestContext& context);
82
83 /// This session's HLE request handlers; if nullptr, this is not an HLE server
84 std::shared_ptr<SessionRequestManager> manager;
85
86 /// When set to True, converts the session to a domain at the end of the command
87 bool convert_to_domain{};
88
89 /// KSession that owns this KServerSession 66 /// KSession that owns this KServerSession
90 KSession* parent{}; 67 KSession* parent{};
91 68
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_)
13 : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} 13 : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
14KSession::~KSession() = default; 14KSession::~KSession() = default;
15 15
16void KSession::Initialize(KClientPort* port_, const std::string& name_, 16void KSession::Initialize(KClientPort* port_, const std::string& name_) {
17 std::shared_ptr<SessionRequestManager> manager_) {
18 // Increment reference count. 17 // Increment reference count.
19 // Because reference count is one on creation, this will result 18 // Because reference count is one on creation, this will result
20 // in a reference count of two. Thus, when both server and client are closed 19 // 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_,
26 KAutoObject::Create(std::addressof(client)); 25 KAutoObject::Create(std::addressof(client));
27 26
28 // Initialize our sub sessions. 27 // Initialize our sub sessions.
29 server.Initialize(this, name_ + ":Server", manager_); 28 server.Initialize(this, name_ + ":Server");
30 client.Initialize(this, name_ + ":Client"); 29 client.Initialize(this, name_ + ":Client");
31 30
32 // Set state and name. 31 // 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:
21 explicit KSession(KernelCore& kernel_); 21 explicit KSession(KernelCore& kernel_);
22 ~KSession() override; 22 ~KSession() override;
23 23
24 void Initialize(KClientPort* port_, const std::string& name_, 24 void Initialize(KClientPort* port_, const std::string& name_);
25 std::shared_ptr<SessionRequestManager> manager_ = nullptr);
26 25
27 void Finalize() override; 26 void Finalize() override;
28 27
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 {
67 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 67 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
68 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 68 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
69 global_handle_table->Initialize(KHandleTable::MaxTableSize); 69 global_handle_table->Initialize(KHandleTable::MaxTableSize);
70 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
71 70
72 is_phantom_mode_for_singlecore = false; 71 is_phantom_mode_for_singlecore = false;
73 72
@@ -93,6 +92,8 @@ struct KernelCore::Impl {
93 } 92 }
94 93
95 RegisterHostThread(); 94 RegisterHostThread();
95
96 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
96 } 97 }
97 98
98 void InitializeCores() { 99 void InitializeCores() {
@@ -191,17 +192,6 @@ struct KernelCore::Impl {
191 } 192 }
192 193
193 void CloseServices() { 194 void CloseServices() {
194 // Close all open server sessions and ports.
195 std::unordered_set<KAutoObject*> server_objects_;
196 {
197 std::scoped_lock lk(server_objects_lock);
198 server_objects_ = server_objects;
199 server_objects.clear();
200 }
201 for (auto* server_object : server_objects_) {
202 server_object->Close();
203 }
204
205 // Ensures all service threads gracefully shutdown. 195 // Ensures all service threads gracefully shutdown.
206 ClearServiceThreads(); 196 ClearServiceThreads();
207 } 197 }
@@ -419,6 +409,8 @@ struct KernelCore::Impl {
419 return this_id; 409 return this_id;
420 } 410 }
421 411
412 static inline thread_local bool is_phantom_mode_for_singlecore{false};
413
422 bool IsPhantomModeForSingleCore() const { 414 bool IsPhantomModeForSingleCore() const {
423 return is_phantom_mode_for_singlecore; 415 return is_phantom_mode_for_singlecore;
424 } 416 }
@@ -775,24 +767,21 @@ struct KernelCore::Impl {
775 return {}; 767 return {};
776 } 768 }
777 769
778 KClientPort* port = &search->second(system.ServiceManager(), system); 770 return &search->second(system.ServiceManager(), system);
779 RegisterServerObject(&port->GetParent()->GetServerPort());
780 return port;
781 } 771 }
782 772
783 void RegisterServerObject(KAutoObject* server_object) { 773 void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
784 std::scoped_lock lk(server_objects_lock); 774 auto search = service_interface_handlers.find(name);
785 server_objects.insert(server_object); 775 if (search == service_interface_handlers.end()) {
786 } 776 return;
777 }
787 778
788 void UnregisterServerObject(KAutoObject* server_object) { 779 search->second(system.ServiceManager(), server_port);
789 std::scoped_lock lk(server_objects_lock);
790 server_objects.erase(server_object);
791 } 780 }
792 781
793 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 782 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
794 const std::string& name) { 783 const std::string& name) {
795 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); 784 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name);
796 785
797 service_threads_manager.QueueWork( 786 service_threads_manager.QueueWork(
798 [this, service_thread]() { service_threads.emplace(service_thread); }); 787 [this, service_thread]() { service_threads.emplace(service_thread); });
@@ -822,7 +811,6 @@ struct KernelCore::Impl {
822 service_thread_barrier.Sync(); 811 service_thread_barrier.Sync();
823 } 812 }
824 813
825 std::mutex server_objects_lock;
826 std::mutex registered_objects_lock; 814 std::mutex registered_objects_lock;
827 std::mutex registered_in_use_objects_lock; 815 std::mutex registered_in_use_objects_lock;
828 816
@@ -853,8 +841,8 @@ struct KernelCore::Impl {
853 /// Map of named ports managed by the kernel, which can be retrieved using 841 /// Map of named ports managed by the kernel, which can be retrieved using
854 /// the ConnectToPort SVC. 842 /// the ConnectToPort SVC.
855 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 843 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
844 std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers;
856 NamedPortTable named_ports; 845 NamedPortTable named_ports;
857 std::unordered_set<KAutoObject*> server_objects;
858 std::unordered_set<KAutoObject*> registered_objects; 846 std::unordered_set<KAutoObject*> registered_objects;
859 std::unordered_set<KAutoObject*> registered_in_use_objects; 847 std::unordered_set<KAutoObject*> registered_in_use_objects;
860 848
@@ -903,7 +891,6 @@ struct KernelCore::Impl {
903 891
904 bool is_multicore{}; 892 bool is_multicore{};
905 std::atomic_bool is_shutting_down{}; 893 std::atomic_bool is_shutting_down{};
906 bool is_phantom_mode_for_singlecore{};
907 u32 single_core_thread_id{}; 894 u32 single_core_thread_id{};
908 895
909 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; 896 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
@@ -1070,16 +1057,17 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
1070 impl->service_interface_factory.emplace(std::move(name), factory); 1057 impl->service_interface_factory.emplace(std::move(name), factory);
1071} 1058}
1072 1059
1073KClientPort* KernelCore::CreateNamedServicePort(std::string name) { 1060void KernelCore::RegisterInterfaceForNamedService(std::string name,
1074 return impl->CreateNamedServicePort(std::move(name)); 1061 ServiceInterfaceHandlerFn&& handler) {
1062 impl->service_interface_handlers.emplace(std::move(name), handler);
1075} 1063}
1076 1064
1077void KernelCore::RegisterServerObject(KAutoObject* server_object) { 1065KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
1078 impl->RegisterServerObject(server_object); 1066 return impl->CreateNamedServicePort(std::move(name));
1079} 1067}
1080 1068
1081void KernelCore::UnregisterServerObject(KAutoObject* server_object) { 1069void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
1082 impl->UnregisterServerObject(server_object); 1070 impl->RegisterNamedServiceHandler(std::move(name), server_port);
1083} 1071}
1084 1072
1085void KernelCore::RegisterKernelObject(KAutoObject* object) { 1073void 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;
48class KProcess; 48class KProcess;
49class KResourceLimit; 49class KResourceLimit;
50class KScheduler; 50class KScheduler;
51class KServerPort;
51class KServerSession; 52class KServerSession;
52class KSession; 53class KSession;
53class KSessionRequest; 54class KSessionRequest;
@@ -67,6 +68,8 @@ class TimeManager;
67using ServiceInterfaceFactory = 68using ServiceInterfaceFactory =
68 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; 69 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
69 70
71using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>;
72
70namespace Init { 73namespace Init {
71struct KSlabResourceCounts; 74struct KSlabResourceCounts;
72} 75}
@@ -196,16 +199,14 @@ public:
196 /// Registers a named HLE service, passing a factory used to open a port to that service. 199 /// Registers a named HLE service, passing a factory used to open a port to that service.
197 void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); 200 void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
198 201
202 /// Registers a setup function for the named HLE service.
203 void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler);
204
199 /// Opens a port to a service previously registered with RegisterNamedService. 205 /// Opens a port to a service previously registered with RegisterNamedService.
200 KClientPort* CreateNamedServicePort(std::string name); 206 KClientPort* CreateNamedServicePort(std::string name);
201 207
202 /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. 208 /// Accepts a session on a port created by CreateNamedServicePort.
203 /// This is necessary because we do not emulate processes for HLE sessions and ports. 209 void RegisterNamedServiceHandler(std::string name, KServerPort* server_port);
204 void RegisterServerObject(KAutoObject* server_object);
205
206 /// Unregisters a server session or port previously registered with RegisterServerSession when
207 /// it was destroyed during the current emulation session.
208 void UnregisterServerObject(KAutoObject* server_object);
209 210
210 /// Registers all kernel objects with the global emulation state, this is purely for tracking 211 /// Registers all kernel objects with the global emulation state, this is purely for tracking
211 /// leaks after emulation has been shutdown. 212 /// 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 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <condition_variable>
5#include <functional> 4#include <functional>
5#include <map>
6#include <mutex> 6#include <mutex>
7#include <thread> 7#include <thread>
8#include <vector> 8#include <vector>
9#include <queue>
10 9
11#include "common/scope_exit.h" 10#include "common/scope_exit.h"
12#include "common/thread.h" 11#include "common/thread.h"
12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_scoped_resource_reservation.h"
13#include "core/hle/kernel/k_session.h" 16#include "core/hle/kernel/k_session.h"
14#include "core/hle/kernel/k_thread.h" 17#include "core/hle/kernel/k_thread.h"
15#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
@@ -19,101 +22,198 @@ namespace Kernel {
19 22
20class ServiceThread::Impl final { 23class ServiceThread::Impl final {
21public: 24public:
22 explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); 25 explicit Impl(KernelCore& kernel, const std::string& service_name);
23 ~Impl(); 26 ~Impl();
24 27
25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 28 void WaitAndProcessImpl();
29 void SessionClosed(KServerSession* server_session,
30 std::shared_ptr<SessionRequestManager> manager);
31 void LoopProcess();
32
33 void RegisterServerSession(KServerSession* session,
34 std::shared_ptr<SessionRequestManager> manager);
26 35
27private: 36private:
28 std::vector<std::jthread> threads; 37 KernelCore& kernel;
29 std::queue<std::function<void()>> requests; 38
30 std::mutex queue_mutex; 39 std::jthread m_thread;
31 std::condition_variable_any condition; 40 std::mutex m_session_mutex;
32 const std::string service_name; 41 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
42 KEvent* m_wakeup_event;
43 KProcess* m_process;
44 std::atomic<bool> m_shutdown_requested;
45 const std::string m_service_name;
33}; 46};
34 47
35ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) 48void ServiceThread::Impl::WaitAndProcessImpl() {
36 : service_name{name} { 49 // Create local list of waitable sessions.
37 for (std::size_t i = 0; i < num_threads; ++i) { 50 std::vector<KSynchronizationObject*> objs;
38 threads.emplace_back([this, &kernel](std::stop_token stop_token) { 51 std::vector<std::shared_ptr<SessionRequestManager>> managers;
39 Common::SetCurrentThreadName(std::string{service_name}.c_str());
40 52
41 // Wait for first request before trying to acquire a render context 53 {
42 { 54 // Lock to get the set.
43 std::unique_lock lock{queue_mutex}; 55 std::scoped_lock lk{m_session_mutex};
44 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
45 }
46 56
47 if (stop_token.stop_requested()) { 57 // Reserve the needed quantity.
48 return; 58 objs.reserve(m_sessions.size() + 1);
49 } 59 managers.reserve(m_sessions.size());
50 60
51 // Allocate a dummy guest thread for this host thread. 61 // Copy to our local list.
52 kernel.RegisterHostThread(); 62 for (const auto& [session, manager] : m_sessions) {
63 objs.push_back(session);
64 managers.push_back(manager);
65 }
53 66
54 while (true) { 67 // Insert the wakeup event at the end.
55 std::function<void()> task; 68 objs.push_back(&m_wakeup_event->GetReadableEvent());
69 }
56 70
57 { 71 // Wait on the list of sessions.
58 std::unique_lock lock{queue_mutex}; 72 s32 index{-1};
59 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 73 Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(),
74 static_cast<s32>(objs.size()), -1);
75 ASSERT(!rc.IsFailure());
76
77 // If this was the wakeup event, clear it and finish.
78 if (index >= static_cast<s64>(objs.size() - 1)) {
79 m_wakeup_event->Clear();
80 return;
81 }
60 82
61 if (stop_token.stop_requested()) { 83 // This event is from a server session.
62 return; 84 auto* server_session = static_cast<KServerSession*>(objs[index]);
63 } 85 auto& manager = managers[index];
64 86
65 if (requests.empty()) { 87 // Fetch the HLE request context.
66 continue; 88 std::shared_ptr<HLERequestContext> context;
67 } 89 rc = server_session->ReceiveRequest(&context, manager);
68 90
69 task = std::move(requests.front()); 91 // If the session was closed, handle that.
70 requests.pop(); 92 if (rc == ResultSessionClosed) {
71 } 93 SessionClosed(server_session, manager);
72 94
73 task(); 95 // Finish.
74 } 96 return;
75 });
76 } 97 }
98
99 // TODO: handle other cases
100 ASSERT(rc == ResultSuccess);
101
102 // Perform the request.
103 Result service_rc = manager->CompleteSyncRequest(server_session, *context);
104
105 // Reply to the client.
106 rc = server_session->SendReplyHLE();
107
108 if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
109 SessionClosed(server_session, manager);
110 return;
111 }
112
113 // TODO: handle other cases
114 ASSERT(rc == ResultSuccess);
115 ASSERT(service_rc == ResultSuccess);
77} 116}
78 117
79void ServiceThread::Impl::QueueSyncRequest(KSession& session, 118void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
80 std::shared_ptr<HLERequestContext>&& context) { 119 std::shared_ptr<SessionRequestManager> manager) {
81 { 120 {
82 std::unique_lock lock{queue_mutex}; 121 // Lock to get the set.
122 std::scoped_lock lk{m_session_mutex};
123
124 // Erase the session.
125 ASSERT(m_sessions.erase(server_session) == 1);
126 }
83 127
84 auto* server_session{&session.GetServerSession()}; 128 // Close our reference to the server session.
129 server_session->Close();
130}
85 131
86 // Open a reference to the session to ensure it is not closes while the service request 132void ServiceThread::Impl::LoopProcess() {
87 // completes asynchronously. 133 Common::SetCurrentThreadName(m_service_name.c_str());
88 server_session->Open();
89 134
90 requests.emplace([server_session, context{std::move(context)}]() { 135 kernel.RegisterHostThread();
91 // Close the reference.
92 SCOPE_EXIT({ server_session->Close(); });
93 136
94 // Complete the service request. 137 while (!m_shutdown_requested.load()) {
95 server_session->CompleteSyncRequest(*context); 138 WaitAndProcessImpl();
96 });
97 } 139 }
98 condition.notify_one(); 140}
141
142void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session,
143 std::shared_ptr<SessionRequestManager> manager) {
144 // Open the server session.
145 server_session->Open();
146
147 {
148 // Lock to get the set.
149 std::scoped_lock lk{m_session_mutex};
150
151 // Insert the session and manager.
152 m_sessions[server_session] = manager;
153 }
154
155 // Signal the wakeup event.
156 m_wakeup_event->Signal();
99} 157}
100 158
101ServiceThread::Impl::~Impl() { 159ServiceThread::Impl::~Impl() {
102 condition.notify_all(); 160 // Shut down the processing thread.
103 for (auto& thread : threads) { 161 m_shutdown_requested.store(true);
104 thread.request_stop(); 162 m_wakeup_event->Signal();
105 thread.join(); 163 m_thread.join();
164
165 // Lock mutex.
166 m_session_mutex.lock();
167
168 // Close all remaining sessions.
169 for (const auto& [server_session, manager] : m_sessions) {
170 server_session->Close();
106 } 171 }
172
173 // Destroy remaining managers.
174 m_sessions.clear();
175
176 // Close event.
177 m_wakeup_event->GetReadableEvent().Close();
178 m_wakeup_event->Close();
179
180 // Close process.
181 m_process->Close();
182}
183
184ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
185 : kernel{kernel_}, m_service_name{service_name} {
186 // Initialize process.
187 m_process = KProcess::Create(kernel);
188 KProcess::Initialize(m_process, kernel.System(), service_name,
189 KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
190
191 // Reserve a new event from the process resource limit
192 KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
193 ASSERT(event_reservation.Succeeded());
194
195 // Initialize event.
196 m_wakeup_event = KEvent::Create(kernel);
197 m_wakeup_event->Initialize(m_process);
198
199 // Commit the event reservation.
200 event_reservation.Commit();
201
202 // Register the event.
203 KEvent::Register(kernel, m_wakeup_event);
204
205 // Start thread.
206 m_thread = std::jthread([this] { LoopProcess(); });
107} 207}
108 208
109ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) 209ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
110 : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} 210 : impl{std::make_unique<Impl>(kernel, name)} {}
111 211
112ServiceThread::~ServiceThread() = default; 212ServiceThread::~ServiceThread() = default;
113 213
114void ServiceThread::QueueSyncRequest(KSession& session, 214void ServiceThread::RegisterServerSession(KServerSession* session,
115 std::shared_ptr<HLERequestContext>&& context) { 215 std::shared_ptr<SessionRequestManager> manager) {
116 impl->QueueSyncRequest(session, std::move(context)); 216 impl->RegisterServerSession(session, manager);
117} 217}
118 218
119} // namespace Kernel 219} // 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 {
11class HLERequestContext; 11class HLERequestContext;
12class KernelCore; 12class KernelCore;
13class KSession; 13class KSession;
14class SessionRequestManager;
14 15
15class ServiceThread final { 16class ServiceThread final {
16public: 17public:
17 explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); 18 explicit ServiceThread(KernelCore& kernel, const std::string& name);
18 ~ServiceThread(); 19 ~ServiceThread();
19 20
20 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 21 void RegisterServerSession(KServerSession* session,
22 std::shared_ptr<SessionRequestManager> manager);
21 23
22private: 24private:
23 class Impl; 25 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 @@
24#include "core/hle/kernel/k_memory_block.h" 24#include "core/hle/kernel/k_memory_block.h"
25#include "core/hle/kernel/k_memory_layout.h" 25#include "core/hle/kernel/k_memory_layout.h"
26#include "core/hle/kernel/k_page_table.h" 26#include "core/hle/kernel/k_page_table.h"
27#include "core/hle/kernel/k_port.h"
27#include "core/hle/kernel/k_process.h" 28#include "core/hle/kernel/k_process.h"
28#include "core/hle/kernel/k_readable_event.h" 29#include "core/hle/kernel/k_readable_event.h"
29#include "core/hle/kernel/k_resource_limit.h" 30#include "core/hle/kernel/k_resource_limit.h"
@@ -382,9 +383,9 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
382 383
383 // Create a session. 384 // Create a session.
384 KClientSession* session{}; 385 KClientSession* session{};
385 R_TRY(port->CreateSession(std::addressof(session), 386 R_TRY(port->CreateSession(std::addressof(session)));
386 std::make_shared<SessionRequestManager>(kernel))); 387
387 port->Close(); 388 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
388 389
389 // Register the session in the table, close the extra reference. 390 // Register the session in the table, close the extra reference.
390 handle_table.Register(*out, session); 391 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
99ServiceFrameworkBase::~ServiceFrameworkBase() { 99ServiceFrameworkBase::~ServiceFrameworkBase() {
100 // Wait for other threads to release access before destroying 100 // Wait for other threads to release access before destroying
101 const auto guard = LockService(); 101 const auto guard = LockService();
102
103 if (named_port != nullptr) {
104 named_port->GetClientPort().Close();
105 named_port->GetServerPort().Close();
106 named_port = nullptr;
107 }
102} 108}
103 109
104void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { 110void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
@@ -113,15 +119,16 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
113Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { 119Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
114 const auto guard = LockService(); 120 const auto guard = LockService();
115 121
116 ASSERT(!service_registered); 122 if (named_port == nullptr) {
123 ASSERT(!service_registered);
117 124
118 auto* port = Kernel::KPort::Create(kernel); 125 named_port = Kernel::KPort::Create(kernel);
119 port->Initialize(max_sessions, false, service_name); 126 named_port->Initialize(max_sessions, false, service_name);
120 port->GetServerPort().SetSessionHandler(shared_from_this());
121 127
122 service_registered = true; 128 service_registered = true;
129 }
123 130
124 return port->GetClientPort(); 131 return named_port->GetClientPort();
125} 132}
126 133
127void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { 134void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
@@ -199,7 +206,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
199 switch (ctx.GetCommandType()) { 206 switch (ctx.GetCommandType()) {
200 case IPC::CommandType::Close: 207 case IPC::CommandType::Close:
201 case IPC::CommandType::TIPC_Close: { 208 case IPC::CommandType::TIPC_Close: {
202 session.Close();
203 IPC::ResponseBuilder rb{ctx, 2}; 209 IPC::ResponseBuilder rb{ctx, 2};
204 rb.Push(ResultSuccess); 210 rb.Push(ResultSuccess);
205 result = IPC::ERR_REMOTE_PROCESS_DEAD; 211 result = IPC::ERR_REMOTE_PROCESS_DEAD;
@@ -244,6 +250,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
244 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 250 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
245 251
246 system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); 252 system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory);
253 system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler);
247 254
248 Account::InstallInterfaces(system); 255 Account::InstallInterfaces(system);
249 AM::InstallInterfaces(*sm, *nv_flinger, system); 256 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;
20namespace Kernel { 20namespace Kernel {
21class HLERequestContext; 21class HLERequestContext;
22class KClientPort; 22class KClientPort;
23class KPort;
23class KServerSession; 24class KServerSession;
24class ServiceThread; 25class ServiceThread;
25} // namespace Kernel 26} // namespace Kernel
@@ -98,6 +99,9 @@ protected:
98 /// Identifier string used to connect to the service. 99 /// Identifier string used to connect to the service.
99 std::string service_name; 100 std::string service_name;
100 101
102 /// Port used by ManageNamedPort.
103 Kernel::KPort* named_port{};
104
101private: 105private:
102 template <typename T> 106 template <typename T>
103 friend class ServiceFramework; 107 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);
23constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); 23constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
24 24
25ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} 25ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
26ServiceManager::~ServiceManager() = default; 26
27ServiceManager::~ServiceManager() {
28 for (auto& [name, port] : service_ports) {
29 port->GetClientPort().Close();
30 port->GetServerPort().Close();
31 }
32}
27 33
28void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { 34void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
29 controller_interface->InvokeRequest(context); 35 controller_interface->InvokeRequest(context);
@@ -43,6 +49,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
43 return self.sm_interface->CreatePort(); 49 return self.sm_interface->CreatePort();
44} 50}
45 51
52void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) {
53 self.sm_interface->AcceptSession(server_port);
54}
55
46Result ServiceManager::RegisterService(std::string name, u32 max_sessions, 56Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
47 Kernel::SessionRequestHandlerPtr handler) { 57 Kernel::SessionRequestHandlerPtr handler) {
48 58
@@ -53,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
53 return ERR_ALREADY_REGISTERED; 63 return ERR_ALREADY_REGISTERED;
54 } 64 }
55 65
56 registered_services.emplace(std::move(name), handler); 66 auto* port = Kernel::KPort::Create(kernel);
67 port->Initialize(ServerSessionCountMax, false, name);
68
69 service_ports.emplace(name, port);
70 registered_services.emplace(name, handler);
57 71
58 return ResultSuccess; 72 return ResultSuccess;
59} 73}
@@ -68,24 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) {
68 } 82 }
69 83
70 registered_services.erase(iter); 84 registered_services.erase(iter);
85 service_ports.erase(name);
86
71 return ResultSuccess; 87 return ResultSuccess;
72} 88}
73 89
74ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { 90ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
75 CASCADE_CODE(ValidateServiceName(name)); 91 CASCADE_CODE(ValidateServiceName(name));
76 auto it = registered_services.find(name); 92 auto it = service_ports.find(name);
77 if (it == registered_services.end()) { 93 if (it == service_ports.end()) {
78 LOG_ERROR(Service_SM, "Server is not registered! service={}", name); 94 LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
79 return ERR_SERVICE_NOT_REGISTERED; 95 return ERR_SERVICE_NOT_REGISTERED;
80 } 96 }
81 97
82 auto* port = Kernel::KPort::Create(kernel); 98 return it->second;
83
84 port->Initialize(ServerSessionCountMax, false, name);
85 auto handler = it->second;
86 port->GetServerPort().SetSessionHandler(std::move(handler));
87
88 return port;
89} 99}
90 100
91/** 101/**
@@ -144,24 +154,20 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
144 154
145 // Find the named port. 155 // Find the named port.
146 auto port_result = service_manager.GetServicePort(name); 156 auto port_result = service_manager.GetServicePort(name);
147 if (port_result.Failed()) { 157 auto service = service_manager.GetService<Kernel::SessionRequestHandler>(name);
158 if (port_result.Failed() || !service) {
148 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); 159 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
149 return port_result.Code(); 160 return port_result.Code();
150 } 161 }
151 auto& port = port_result.Unwrap(); 162 auto& port = port_result.Unwrap();
152 SCOPE_EXIT({
153 port->GetClientPort().Close();
154 port->GetServerPort().Close();
155 });
156 163
157 // Create a new session. 164 // Create a new session.
158 Kernel::KClientSession* session{}; 165 Kernel::KClientSession* session{};
159 if (const auto result = port->GetClientPort().CreateSession( 166 if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
160 std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel));
161 result.IsError()) {
162 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); 167 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
163 return result; 168 return result;
164 } 169 }
170 service->AcceptSession(&port->GetServerPort());
165 171
166 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); 172 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
167 173
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:
51class ServiceManager { 51class ServiceManager {
52public: 52public:
53 static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); 53 static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system);
54 static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port);
54 55
55 explicit ServiceManager(Kernel::KernelCore& kernel_); 56 explicit ServiceManager(Kernel::KernelCore& kernel_);
56 ~ServiceManager(); 57 ~ServiceManager();
@@ -78,6 +79,7 @@ private:
78 79
79 /// Map of registered services, retrieved using GetServicePort. 80 /// Map of registered services, retrieved using GetServicePort.
80 std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services; 81 std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
82 std::unordered_map<std::string, Kernel::KPort*> service_ports;
81 83
82 /// Kernel context 84 /// Kernel context
83 Kernel::KernelCore& kernel; 85 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 @@
15namespace Service::SM { 15namespace Service::SM {
16 16
17void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { 17void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
18 ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), 18 ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain");
19 "Session is already a domain");
20 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); 19 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
21 ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); 20 ctx.GetManager()->ConvertToDomainOnRequestEnd();
22 21
23 IPC::ResponseBuilder rb{ctx, 3}; 22 IPC::ResponseBuilder rb{ctx, 3};
24 rb.Push(ResultSuccess); 23 rb.Push(ResultSuccess);
@@ -29,9 +28,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
29 LOG_DEBUG(Service, "called"); 28 LOG_DEBUG(Service, "called");
30 29
31 auto& process = *ctx.GetThread().GetOwnerProcess(); 30 auto& process = *ctx.GetThread().GetOwnerProcess();
32 auto& parent_session = *ctx.Session()->GetParent(); 31 auto session_manager = ctx.GetManager();
33 auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager();
34 auto& session_handler = session_manager->SessionHandler();
35 32
36 // FIXME: this is duplicated from the SVC, it should just call it instead 33 // FIXME: this is duplicated from the SVC, it should just call it instead
37 // once this is a proper process 34 // once this is a proper process
@@ -46,13 +43,14 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
46 ASSERT(session != nullptr); 43 ASSERT(session != nullptr);
47 44
48 // Initialize the session. 45 // Initialize the session.
49 session->Initialize(nullptr, parent_session.GetName(), session_manager); 46 session->Initialize(nullptr, "");
50 47
51 // Commit the session reservation. 48 // Commit the session reservation.
52 session_reservation.Commit(); 49 session_reservation.Commit();
53 50
54 // Register the session. 51 // Register with manager.
55 session_handler.ClientConnected(&session->GetServerSession()); 52 session_manager->SessionHandler().RegisterSession(&session->GetServerSession(),
53 session_manager);
56 54
57 // We succeeded. 55 // We succeeded.
58 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 56 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 @@
11#include "core/internal_network/network_interface.h" 11#include "core/internal_network/network_interface.h"
12#include "core/internal_network/socket_proxy.h" 12#include "core/internal_network/socket_proxy.h"
13 13
14#if YUZU_UNIX
15#include <sys/socket.h>
16#endif
17
14namespace Network { 18namespace Network {
15 19
16ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} 20ProxySocket::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
224 ir_opt/lower_fp16_to_fp32.cpp 224 ir_opt/lower_fp16_to_fp32.cpp
225 ir_opt/lower_int64_to_int32.cpp 225 ir_opt/lower_int64_to_int32.cpp
226 ir_opt/passes.h 226 ir_opt/passes.h
227 ir_opt/position_pass.cpp
227 ir_opt/rescaling_pass.cpp 228 ir_opt/rescaling_pass.cpp
228 ir_opt/ssa_rewrite_pass.cpp 229 ir_opt/ssa_rewrite_pass.cpp
229 ir_opt/texture_pass.cpp 230 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
450 if (program.info.uses_rescaling_uniform) { 450 if (program.info.uses_rescaling_uniform) {
451 header += "PARAM scaling[1]={program.local[0..0]};"; 451 header += "PARAM scaling[1]={program.local[0..0]};";
452 } 452 }
453 if (program.info.uses_render_area) {
454 header += "PARAM render_area[1]={program.local[1..1]};";
455 }
453 header += "TEMP "; 456 header += "TEMP ";
454 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) { 457 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
455 header += fmt::format("R{},", index); 458 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) {
43 Alias(inst, value); 43 Alias(inst, value);
44} 44}
45 45
46void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value);
48}
49
46void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { 50void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value); 51 Alias(inst, value);
48} 52}
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) {
396 ctx.Add("MOV.F {}.x,scaling[0].z;", inst); 396 ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
397} 397}
398 398
399void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) {
400 ctx.Add("MOV.F {},render_area[0];", inst);
401}
402
399void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { 403void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
400 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); 404 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
401} 405}
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);
73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
74void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 74void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
75void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); 75void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
76void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
76void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset); 77void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset);
77void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value); 78void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value);
78void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); 79void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
@@ -195,6 +196,7 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist
195void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 196void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
196void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 197void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
197void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 198void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
198void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 200void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 201void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 202void 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)
48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); 48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
49} 49}
50 50
51void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
52 ctx.AddF32("{}=ftoi({});", inst, value);
53}
54
51void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { 55void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
52 NotImplemented(); 56 NotImplemented();
53} 57}
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) {
416 ctx.AddF32("{}=scaling.z;", inst); 416 ctx.AddF32("{}=scaling.z;", inst);
417} 417}
418 418
419void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) {
420 ctx.AddF32x4("{}=render_area;", inst);
421}
422
419void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { 423void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
420 ctx.AddU32("{}=lmem[{}];", inst, word_offset); 424 ctx.AddU32("{}=lmem[{}];", inst, word_offset);
421} 425}
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);
87void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 87void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
88void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 88void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
89void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); 89void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
90void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
90void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); 91void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset);
91void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); 92void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value);
92void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); 93void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
@@ -229,6 +230,7 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
229void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); 230void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
230void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 231void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
231void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 232void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
233void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
232void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); 234void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
233void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 235void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 236void 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
358 if (info.uses_rescaling_uniform) { 358 if (info.uses_rescaling_uniform) {
359 header += "layout(location=0) uniform vec4 scaling;"; 359 header += "layout(location=0) uniform vec4 scaling;";
360 } 360 }
361 if (info.uses_render_area) {
362 header += "layout(location=1) uniform vec4 render_area;";
363 }
361 DefineConstantBuffers(bindings); 364 DefineConstantBuffers(bindings);
362 DefineConstantBufferIndirect(); 365 DefineConstantBufferIndirect();
363 DefineStorageBuffers(bindings); 366 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 {
23 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; 23 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
24 u32 down_factor; 24 u32 down_factor;
25}; 25};
26struct RenderAreaLayout {
27 std::array<f32, 4> render_area;
28};
26constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); 29constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
27constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); 30constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
31constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area);
28 32
29[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, 33[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
30 IR::Program& program, Bindings& bindings); 34 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&) {
18 throw NotImplementedException("SPIR-V Instruction"); 18 throw NotImplementedException("SPIR-V Instruction");
19} 19}
20 20
21void EmitBitCastS32F32(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction");
23}
24
21void EmitBitCastF16U16(EmitContext&) { 25void EmitBitCastF16U16(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction"); 26 throw NotImplementedException("SPIR-V Instruction");
23} 27}
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) {
353 case IR::Attribute::TessellationEvaluationPointV: 353 case IR::Attribute::TessellationEvaluationPointV:
354 return ctx.OpLoad(ctx.F32[1], 354 return ctx.OpLoad(ctx.F32[1],
355 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); 355 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U)));
356
357 default: 356 default:
358 throw NotImplementedException("Read attribute {}", attr); 357 throw NotImplementedException("Read attribute {}", attr);
359 } 358 }
@@ -537,6 +536,17 @@ Id EmitResolutionDownFactor(EmitContext& ctx) {
537 } 536 }
538} 537}
539 538
539Id EmitRenderArea(EmitContext& ctx) {
540 if (ctx.profile.unified_descriptor_binding) {
541 const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[4])};
542 const Id index{ctx.Const(ctx.render_are_member_index)};
543 const Id pointer{ctx.OpAccessChain(pointer_type, ctx.render_area_push_constant, index)};
544 return ctx.OpLoad(ctx.F32[4], pointer);
545 } else {
546 throw NotImplementedException("SPIR-V Instruction");
547 }
548}
549
540Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { 550Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
541 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; 551 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
542 return ctx.OpLoad(ctx.U32[1], pointer); 552 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);
76Id EmitIsHelperInvocation(EmitContext& ctx); 76Id EmitIsHelperInvocation(EmitContext& ctx);
77Id EmitYDirection(EmitContext& ctx); 77Id EmitYDirection(EmitContext& ctx);
78Id EmitResolutionDownFactor(EmitContext& ctx); 78Id EmitResolutionDownFactor(EmitContext& ctx);
79Id EmitRenderArea(EmitContext& ctx);
79Id EmitLoadLocal(EmitContext& ctx, Id word_offset); 80Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
80void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); 81void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
81Id EmitUndefU1(EmitContext& ctx); 82Id EmitUndefU1(EmitContext& ctx);
@@ -177,7 +178,8 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
177void EmitBitCastU16F16(EmitContext& ctx); 178void EmitBitCastU16F16(EmitContext& ctx);
178Id EmitBitCastU32F32(EmitContext& ctx, Id value); 179Id EmitBitCastU32F32(EmitContext& ctx, Id value);
179void EmitBitCastU64F64(EmitContext& ctx); 180void EmitBitCastU64F64(EmitContext& ctx);
180void EmitBitCastF16U16(EmitContext& ctx); 181void EmitBitCastS32F32(EmitContext& ctx);
182void EmitBitCastF16U16(EmitContext&);
181Id EmitBitCastF32U32(EmitContext& ctx, Id value); 183Id EmitBitCastF32U32(EmitContext& ctx, Id value);
182void EmitBitCastF64U64(EmitContext& ctx); 184void EmitBitCastF64U64(EmitContext& ctx);
183Id EmitPackUint2x32(EmitContext& ctx, Id value); 185Id 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
473 DefineAttributeMemAccess(program.info); 473 DefineAttributeMemAccess(program.info);
474 DefineGlobalMemoryFunctions(program.info); 474 DefineGlobalMemoryFunctions(program.info);
475 DefineRescalingInput(program.info); 475 DefineRescalingInput(program.info);
476 DefineRenderArea(program.info);
476} 477}
477 478
478EmitContext::~EmitContext() = default; 479EmitContext::~EmitContext() = default;
@@ -982,6 +983,36 @@ void EmitContext::DefineRescalingInputUniformConstant() {
982 } 983 }
983} 984}
984 985
986void EmitContext::DefineRenderArea(const Info& info) {
987 if (!info.uses_render_area) {
988 return;
989 }
990
991 if (profile.unified_descriptor_binding) {
992 boost::container::static_vector<Id, 1> members{};
993 u32 member_index{0};
994
995 members.push_back(F32[4]);
996 render_are_member_index = member_index++;
997
998 const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))};
999 Decorate(push_constant_struct, spv::Decoration::Block);
1000 Name(push_constant_struct, "RenderAreaInfo");
1001
1002 MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0);
1003 MemberName(push_constant_struct, render_are_member_index, "render_area");
1004
1005 const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)};
1006 render_area_push_constant =
1007 AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant);
1008 Name(render_area_push_constant, "render_area_push_constants");
1009
1010 if (profile.supported_spirv >= 0x00010400) {
1011 interfaces.push_back(render_area_push_constant);
1012 }
1013 }
1014}
1015
985void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { 1016void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
986 if (info.constant_buffer_descriptors.empty()) { 1017 if (info.constant_buffer_descriptors.empty()) {
987 return; 1018 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:
243 u32 texture_rescaling_index{}; 243 u32 texture_rescaling_index{};
244 u32 image_rescaling_index{}; 244 u32 image_rescaling_index{};
245 245
246 Id render_area_push_constant{};
247 u32 render_are_member_index{};
248
246 Id local_memory{}; 249 Id local_memory{};
247 250
248 Id shared_memory_u8{}; 251 Id shared_memory_u8{};
@@ -318,6 +321,7 @@ private:
318 void DefineRescalingInput(const Info& info); 321 void DefineRescalingInput(const Info& info);
319 void DefineRescalingInputPushConstant(); 322 void DefineRescalingInputPushConstant();
320 void DefineRescalingInputUniformConstant(); 323 void DefineRescalingInputUniformConstant();
324 void DefineRenderArea(const Info& info);
321 325
322 void DefineInputs(const IR::Program& program); 326 void DefineInputs(const IR::Program& program);
323 void DefineOutputs(const IR::Program& program); 327 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:
22 22
23 [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; 23 [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
24 24
25 [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
26
27 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
28
25 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; 29 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
26 30
27 [[nodiscard]] virtual u32 LocalMemorySize() const = 0; 31 [[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() {
378 return Inst<F32>(Opcode::ResolutionDownFactor); 378 return Inst<F32>(Opcode::ResolutionDownFactor);
379} 379}
380 380
381F32 IREmitter::RenderAreaWidth() {
382 return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 0));
383}
384
385F32 IREmitter::RenderAreaHeight() {
386 return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 1));
387}
388
381U32 IREmitter::LaneId() { 389U32 IREmitter::LaneId() {
382 return Inst<U32>(Opcode::LaneId); 390 return Inst<U32>(Opcode::LaneId);
383} 391}
@@ -684,6 +692,11 @@ IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) {
684} 692}
685 693
686template <> 694template <>
695IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) {
696 return Inst<IR::S32>(Opcode::BitCastS32F32, value);
697}
698
699template <>
687IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { 700IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) {
688 return Inst<IR::F32>(Opcode::BitCastF32U32, value); 701 return Inst<IR::F32>(Opcode::BitCastF32U32, value);
689} 702}
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:
103 103
104 [[nodiscard]] F32 ResolutionDownFactor(); 104 [[nodiscard]] F32 ResolutionDownFactor();
105 105
106 [[nodiscard]] F32 RenderAreaWidth();
107 [[nodiscard]] F32 RenderAreaHeight();
108
106 [[nodiscard]] U32 LaneId(); 109 [[nodiscard]] U32 LaneId();
107 110
108 [[nodiscard]] U32 LoadGlobalU8(const U64& address); 111 [[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};
37constexpr Type U16{Type::U16}; 37constexpr Type U16{Type::U16};
38constexpr Type U32{Type::U32}; 38constexpr Type U32{Type::U32};
39constexpr Type U64{Type::U64}; 39constexpr Type U64{Type::U64};
40constexpr Type S32{Type::S32};
40constexpr Type F16{Type::F16}; 41constexpr Type F16{Type::F16};
41constexpr Type F32{Type::F32}; 42constexpr Type F32{Type::F32};
42constexpr Type F64{Type::F64}; 43constexpr 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,
63OPCODE(IsHelperInvocation, U1, ) 63OPCODE(IsHelperInvocation, U1, )
64OPCODE(YDirection, F32, ) 64OPCODE(YDirection, F32, )
65OPCODE(ResolutionDownFactor, F32, ) 65OPCODE(ResolutionDownFactor, F32, )
66OPCODE(RenderArea, F32x4, )
66 67
67// Undefined 68// Undefined
68OPCODE(UndefU1, U1, ) 69OPCODE(UndefU1, U1, )
@@ -173,6 +174,7 @@ OPCODE(SelectF64, F64, U1,
173OPCODE(BitCastU16F16, U16, F16, ) 174OPCODE(BitCastU16F16, U16, F16, )
174OPCODE(BitCastU32F32, U32, F32, ) 175OPCODE(BitCastU32F32, U32, F32, )
175OPCODE(BitCastU64F64, U64, F64, ) 176OPCODE(BitCastU64F64, U64, F64, )
177OPCODE(BitCastS32F32, S32, F32, )
176OPCODE(BitCastF16U16, F16, U16, ) 178OPCODE(BitCastF16U16, F16, U16, )
177OPCODE(BitCastF32U32, F32, U32, ) 179OPCODE(BitCastF32U32, F32, U32, )
178OPCODE(BitCastF64U64, F64, U64, ) 180OPCODE(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 {
24 U16 = 1 << 7, 24 U16 = 1 << 7,
25 U32 = 1 << 8, 25 U32 = 1 << 8,
26 U64 = 1 << 9, 26 U64 = 1 << 9,
27 F16 = 1 << 10, 27 S32 = 1 << 10,
28 F32 = 1 << 11, 28 F16 = 1 << 11,
29 F64 = 1 << 12, 29 F32 = 1 << 12,
30 U32x2 = 1 << 13, 30 F64 = 1 << 13,
31 U32x3 = 1 << 14, 31 U32x2 = 1 << 14,
32 U32x4 = 1 << 15, 32 U32x3 = 1 << 15,
33 F16x2 = 1 << 16, 33 U32x4 = 1 << 16,
34 F16x3 = 1 << 17, 34 F16x2 = 1 << 17,
35 F16x4 = 1 << 18, 35 F16x3 = 1 << 18,
36 F32x2 = 1 << 19, 36 F16x4 = 1 << 19,
37 F32x3 = 1 << 20, 37 F32x2 = 1 << 20,
38 F32x4 = 1 << 21, 38 F32x3 = 1 << 21,
39 F64x2 = 1 << 22, 39 F32x4 = 1 << 22,
40 F64x3 = 1 << 23, 40 F64x2 = 1 << 23,
41 F64x4 = 1 << 24, 41 F64x3 = 1 << 24,
42 F64x4 = 1 << 25,
42}; 43};
43DECLARE_ENUM_FLAG_OPERATORS(Type) 44DECLARE_ENUM_FLAG_OPERATORS(Type)
44 45
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} {}
23 23
24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} 24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
25 25
26Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {}
27
26Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} 28Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
27 29
28Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} 30Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
@@ -69,6 +71,7 @@ bool Value::operator==(const Value& other) const {
69 return imm_u16 == other.imm_u16; 71 return imm_u16 == other.imm_u16;
70 case Type::U32: 72 case Type::U32:
71 case Type::F32: 73 case Type::F32:
74 case Type::S32:
72 return imm_u32 == other.imm_u32; 75 return imm_u32 == other.imm_u32;
73 case Type::U64: 76 case Type::U64:
74 case Type::F64: 77 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:
44 explicit Value(u8 value) noexcept; 44 explicit Value(u8 value) noexcept;
45 explicit Value(u16 value) noexcept; 45 explicit Value(u16 value) noexcept;
46 explicit Value(u32 value) noexcept; 46 explicit Value(u32 value) noexcept;
47 explicit Value(s32 value) noexcept;
47 explicit Value(f32 value) noexcept; 48 explicit Value(f32 value) noexcept;
48 explicit Value(u64 value) noexcept; 49 explicit Value(u64 value) noexcept;
49 explicit Value(f64 value) noexcept; 50 explicit Value(f64 value) noexcept;
@@ -66,6 +67,7 @@ public:
66 [[nodiscard]] u8 U8() const; 67 [[nodiscard]] u8 U8() const;
67 [[nodiscard]] u16 U16() const; 68 [[nodiscard]] u16 U16() const;
68 [[nodiscard]] u32 U32() const; 69 [[nodiscard]] u32 U32() const;
70 [[nodiscard]] s32 S32() const;
69 [[nodiscard]] f32 F32() const; 71 [[nodiscard]] f32 F32() const;
70 [[nodiscard]] u64 U64() const; 72 [[nodiscard]] u64 U64() const;
71 [[nodiscard]] f64 F64() const; 73 [[nodiscard]] f64 F64() const;
@@ -85,6 +87,7 @@ private:
85 u8 imm_u8; 87 u8 imm_u8;
86 u16 imm_u16; 88 u16 imm_u16;
87 u32 imm_u32; 89 u32 imm_u32;
90 s32 imm_s32;
88 f32 imm_f32; 91 f32 imm_f32;
89 u64 imm_u64; 92 u64 imm_u64;
90 f64 imm_f64; 93 f64 imm_f64;
@@ -266,6 +269,7 @@ using U8 = TypedValue<Type::U8>;
266using U16 = TypedValue<Type::U16>; 269using U16 = TypedValue<Type::U16>;
267using U32 = TypedValue<Type::U32>; 270using U32 = TypedValue<Type::U32>;
268using U64 = TypedValue<Type::U64>; 271using U64 = TypedValue<Type::U64>;
272using S32 = TypedValue<Type::S32>;
269using F16 = TypedValue<Type::F16>; 273using F16 = TypedValue<Type::F16>;
270using F32 = TypedValue<Type::F32>; 274using F32 = TypedValue<Type::F32>;
271using F64 = TypedValue<Type::F64>; 275using F64 = TypedValue<Type::F64>;
@@ -377,6 +381,14 @@ inline u32 Value::U32() const {
377 return imm_u32; 381 return imm_u32;
378} 382}
379 383
384inline s32 Value::S32() const {
385 if (IsIdentity()) {
386 return inst->Arg(0).S32();
387 }
388 DEBUG_ASSERT(type == Type::S32);
389 return imm_s32;
390}
391
380inline f32 Value::F32() const { 392inline f32 Value::F32() const {
381 if (IsIdentity()) { 393 if (IsIdentity()) {
382 return inst->Arg(0).F32(); 394 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
220 220
221 Optimization::ConstantPropagationPass(program); 221 Optimization::ConstantPropagationPass(program);
222 222
223 Optimization::PositionPass(env, program);
224
223 Optimization::GlobalMemoryToStorageBufferPass(program); 225 Optimization::GlobalMemoryToStorageBufferPass(program);
224 Optimization::TexturePass(env, program); 226 Optimization::TexturePass(env, program);
225 227
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);
17void LowerInt64ToInt32(IR::Program& program); 17void LowerInt64ToInt32(IR::Program& program);
18void RescalingPass(IR::Program& program); 18void RescalingPass(IR::Program& program);
19void SsaRewritePass(IR::Program& program); 19void SsaRewritePass(IR::Program& program);
20void PositionPass(Environment& env, IR::Program& program);
20void TexturePass(Environment& env, IR::Program& program); 21void TexturePass(Environment& env, IR::Program& program);
21void VerificationPass(const IR::Program& program); 22void VerificationPass(const IR::Program& program);
22 23
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 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <boost/container/small_vector.hpp>
5
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/ir/ir_emitter.h"
8#include "shader_recompiler/frontend/ir/value.h"
9#include "shader_recompiler/ir_opt/passes.h"
10
11namespace Shader::Optimization {
12
13namespace {
14struct PositionInst {
15 IR::Inst* inst;
16 IR::Block* block;
17 IR::Attribute attr;
18};
19using PositionInstVector = boost::container::small_vector<PositionInst, 24>;
20} // Anonymous namespace
21
22void PositionPass(Environment& env, IR::Program& program) {
23 if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) {
24 return;
25 }
26
27 Info& info{program.info};
28 info.uses_render_area = true;
29
30 PositionInstVector to_replace;
31 for (IR::Block* const block : program.post_order_blocks) {
32 for (IR::Inst& inst : block->Instructions()) {
33 switch (inst.GetOpcode()) {
34 case IR::Opcode::SetAttribute: {
35 const IR::Attribute attr{inst.Arg(0).Attribute()};
36 switch (attr) {
37 case IR::Attribute::PositionX:
38 case IR::Attribute::PositionY: {
39 to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr});
40 break;
41 }
42 default:
43 break;
44 }
45 break;
46 }
47 default:
48 break;
49 }
50 }
51 }
52
53 for (PositionInst& position_inst : to_replace) {
54 IR::IREmitter ir{*position_inst.block,
55 IR::Block::InstructionList::s_iterator_to(*position_inst.inst)};
56 const IR::F32 value(position_inst.inst->Arg(1));
57 const IR::F32F64 scale(ir.Imm32(2.f));
58 const IR::F32 negative_one{ir.Imm32(-1.f)};
59 switch (position_inst.attr) {
60 case IR::Attribute::PositionX: {
61 position_inst.inst->SetArg(
62 1,
63 ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one));
64 break;
65 }
66 case IR::Attribute::PositionY: {
67 position_inst.inst->SetArg(
68 1,
69 ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one));
70 break;
71 }
72 default:
73 break;
74 }
75 }
76}
77} // 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 @@
7 7
8#include <boost/container/small_vector.hpp> 8#include <boost/container/small_vector.hpp>
9 9
10#include "common/settings.h"
10#include "shader_recompiler/environment.h" 11#include "shader_recompiler/environment.h"
11#include "shader_recompiler/frontend/ir/basic_block.h" 12#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/breadth_first_search.h" 13#include "shader_recompiler/frontend/ir/breadth_first_search.h"
@@ -363,6 +364,14 @@ TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
363 return env.ReadTextureType(lhs_raw | rhs_raw); 364 return env.ReadTextureType(lhs_raw | rhs_raw);
364} 365}
365 366
367TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
368 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
369 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
370 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
371 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
372 return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
373}
374
366class Descriptors { 375class Descriptors {
367public: 376public:
368 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, 377 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -451,6 +460,38 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
451 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)), 460 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)),
452 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); 461 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
453} 462}
463
464void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
465 const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
466 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
467 auto get_max_value = [pixel_format]() -> float {
468 switch (pixel_format) {
469 case TexturePixelFormat::A8B8G8R8_SNORM:
470 case TexturePixelFormat::R8G8_SNORM:
471 case TexturePixelFormat::R8_SNORM:
472 return 1.f / std::numeric_limits<char>::max();
473 case TexturePixelFormat::R16G16B16A16_SNORM:
474 case TexturePixelFormat::R16G16_SNORM:
475 case TexturePixelFormat::R16_SNORM:
476 return 1.f / std::numeric_limits<short>::max();
477 default:
478 throw InvalidArgument("Invalid texture pixel format");
479 }
480 };
481
482 const IR::Value new_inst{&*block.PrependNewInst(it, inst)};
483 const IR::F32 x(ir.CompositeExtract(new_inst, 0));
484 const IR::F32 y(ir.CompositeExtract(new_inst, 1));
485 const IR::F32 z(ir.CompositeExtract(new_inst, 2));
486 const IR::F32 w(ir.CompositeExtract(new_inst, 3));
487 const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
488 const IR::Value converted =
489 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value),
490 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value),
491 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value),
492 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value));
493 inst.ReplaceUsesWith(converted);
494}
454} // Anonymous namespace 495} // Anonymous namespace
455 496
456void TexturePass(Environment& env, IR::Program& program) { 497void TexturePass(Environment& env, IR::Program& program) {
@@ -597,6 +638,14 @@ void TexturePass(Environment& env, IR::Program& program) {
597 } else { 638 } else {
598 inst->SetArg(0, IR::Value{}); 639 inst->SetArg(0, IR::Value{});
599 } 640 }
641
642 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
643 inst->GetOpcode() == IR::Opcode::ImageFetch && flags.type == TextureType::Buffer) {
644 const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
645 if (pixel_format != TexturePixelFormat::OTHER) {
646 PathTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
647 }
648 }
600 } 649 }
601} 650}
602 651
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 {
29}; 29};
30constexpr u32 NUM_TEXTURE_TYPES = 9; 30constexpr u32 NUM_TEXTURE_TYPES = 9;
31 31
32enum class TexturePixelFormat : u32 {
33 A8B8G8R8_SNORM,
34 R8_SNORM,
35 R8G8_SNORM,
36 R16G16B16A16_SNORM,
37 R16G16_SNORM,
38 R16_SNORM,
39 OTHER
40};
41
32enum class ImageFormat : u32 { 42enum class ImageFormat : u32 {
33 Typeless, 43 Typeless,
34 R8_UINT, 44 R8_UINT,
@@ -182,6 +192,7 @@ struct Info {
182 bool uses_shadow_lod{}; 192 bool uses_shadow_lod{};
183 bool uses_rescaling_uniform{}; 193 bool uses_rescaling_uniform{};
184 bool uses_cbuf_indirect{}; 194 bool uses_cbuf_indirect{};
195 bool uses_render_area{};
185 196
186 IR::Type used_constant_buffer_types{}; 197 IR::Type used_constant_buffer_types{};
187 IR::Type used_storage_buffer_types{}; 198 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{
29[[nodiscard]] GLenum GetTextureBufferFormat(GLenum gl_format) { 29[[nodiscard]] GLenum GetTextureBufferFormat(GLenum gl_format) {
30 switch (gl_format) { 30 switch (gl_format) {
31 case GL_RGBA8_SNORM: 31 case GL_RGBA8_SNORM:
32 return GL_RGBA8; 32 return GL_RGBA8I;
33 case GL_R8_SNORM: 33 case GL_R8_SNORM:
34 return GL_R8; 34 return GL_R8I;
35 case GL_RGBA16_SNORM: 35 case GL_RGBA16_SNORM:
36 return GL_RGBA16; 36 return GL_RGBA16I;
37 case GL_R16_SNORM: 37 case GL_R16_SNORM:
38 return GL_R16; 38 return GL_R16I;
39 case GL_RG16_SNORM: 39 case GL_RG16_SNORM:
40 return GL_RG16; 40 return GL_RG16I;
41 case GL_RG8_SNORM: 41 case GL_RG8_SNORM:
42 return GL_RG8; 42 return GL_RG8I;
43 default: 43 default:
44 return gl_format; 44 return gl_format;
45 } 45 }
@@ -96,9 +96,6 @@ GLuint Buffer::View(u32 offset, u32 size, PixelFormat format) {
96 texture.Create(GL_TEXTURE_BUFFER); 96 texture.Create(GL_TEXTURE_BUFFER);
97 const GLenum gl_format{MaxwellToGL::GetFormatTuple(format).internal_format}; 97 const GLenum gl_format{MaxwellToGL::GetFormatTuple(format).internal_format};
98 const GLenum texture_format{GetTextureBufferFormat(gl_format)}; 98 const GLenum texture_format{GetTextureBufferFormat(gl_format)};
99 if (texture_format != gl_format) {
100 LOG_WARNING(Render_OpenGL, "Emulating SNORM texture buffer with UNORM.");
101 }
102 glTextureBufferRange(texture.handle, texture_format, buffer.handle, offset, size); 99 glTextureBufferRange(texture.handle, texture_format, buffer.handle, offset, size);
103 views.push_back({ 100 views.push_back({
104 .offset = offset, 101 .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) {
503 float_image_scaling_mask, down_factor, 0.0f); 503 float_image_scaling_mask, down_factor, 0.0f);
504 } 504 }
505 } 505 }
506 if (info.uses_render_area) {
507 const auto render_area_width(static_cast<GLfloat>(regs.surface_clip.width));
508 const auto render_area_height(static_cast<GLfloat>(regs.surface_clip.height));
509 if (use_assembly) {
510 glProgramLocalParameter4fARB(AssemblyStage(stage), 1, render_area_width,
511 render_area_height, 0.0f, 0.0f);
512 } else {
513 glProgramUniform4f(source_programs[stage].handle, 1, render_area_width,
514 render_area_height, 0.0f, 0.0f);
515 }
516 }
506 }}; 517 }};
507 if constexpr (Spec::enabled_stages[0]) { 518 if constexpr (Spec::enabled_stages[0]) {
508 prepare_stage(0); 519 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() {
618 } 618 }
619 flags[Dirty::Viewport0 + index] = false; 619 flags[Dirty::Viewport0 + index] = false;
620 620
621 if (!regs.viewport_scale_offset_enbled) {
622 const auto x = static_cast<GLfloat>(regs.surface_clip.x);
623 const auto y = static_cast<GLfloat>(regs.surface_clip.y);
624 const auto width = static_cast<GLfloat>(regs.surface_clip.width);
625 const auto height = static_cast<GLfloat>(regs.surface_clip.height);
626 glViewportIndexedf(static_cast<GLuint>(index), x, y, width != 0.0f ? width : 1.0f,
627 height != 0.0f ? height : 1.0f);
628 continue;
629 }
630
621 const auto& src = regs.viewport_transform[index]; 631 const auto& src = regs.viewport_transform[index];
622 GLfloat x = conv(src.translate_x - src.scale_x); 632 GLfloat x = conv(src.translate_x - src.scale_x);
623 GLfloat y = conv(src.translate_y - src.scale_y); 633 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;
49using VideoCommon::SerializePipeline; 49using VideoCommon::SerializePipeline;
50using Context = ShaderContext::Context; 50using Context = ShaderContext::Context;
51 51
52constexpr u32 CACHE_VERSION = 6; 52constexpr u32 CACHE_VERSION = 7;
53 53
54template <typename Container> 54template <typename Container>
55auto MakeSpan(Container& container) { 55auto 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:
68 } 68 }
69 69
70 vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const { 70 vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const {
71 using Shader::Backend::SPIRV::RenderAreaLayout;
71 using Shader::Backend::SPIRV::RescalingLayout; 72 using Shader::Backend::SPIRV::RescalingLayout;
72 const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u; 73 const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u;
73 const VkPushConstantRange range{ 74 const VkPushConstantRange range{
74 .stageFlags = static_cast<VkShaderStageFlags>( 75 .stageFlags = static_cast<VkShaderStageFlags>(
75 is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS), 76 is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS),
76 .offset = 0, 77 .offset = 0,
77 .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset, 78 .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset +
79 static_cast<u32>(sizeof(RenderAreaLayout)),
78 }; 80 };
79 return device->GetLogical().CreatePipelineLayout({ 81 return device->GetLogical().CreatePipelineLayout({
80 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 82 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@@ -167,6 +169,12 @@ private:
167 u32 image_bit{1u}; 169 u32 image_bit{1u};
168}; 170};
169 171
172class RenderAreaPushConstant {
173public:
174 bool uses_render_area{};
175 std::array<f32, 4> words{};
176};
177
170inline void PushImageDescriptors(TextureCache& texture_cache, 178inline void PushImageDescriptors(TextureCache& texture_cache,
171 UpdateDescriptorQueue& update_descriptor_queue, 179 UpdateDescriptorQueue& update_descriptor_queue,
172 const Shader::Info& info, RescalingPushConstant& rescaling, 180 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 {
31using boost::container::small_vector; 31using boost::container::small_vector;
32using boost::container::static_vector; 32using boost::container::static_vector;
33using Shader::ImageBufferDescriptor; 33using Shader::ImageBufferDescriptor;
34using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET;
34using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET; 35using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET;
35using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; 36using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
36using Tegra::Texture::TexturePair; 37using Tegra::Texture::TexturePair;
@@ -433,12 +434,19 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
433 update_descriptor_queue.Acquire(); 434 update_descriptor_queue.Acquire();
434 435
435 RescalingPushConstant rescaling; 436 RescalingPushConstant rescaling;
437 RenderAreaPushConstant render_area;
436 const VkSampler* samplers_it{samplers.data()}; 438 const VkSampler* samplers_it{samplers.data()};
437 const VideoCommon::ImageViewInOut* views_it{views.data()}; 439 const VideoCommon::ImageViewInOut* views_it{views.data()};
438 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 440 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
439 buffer_cache.BindHostStageBuffers(stage); 441 buffer_cache.BindHostStageBuffers(stage);
440 PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling, 442 PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling,
441 samplers_it, views_it); 443 samplers_it, views_it);
444 const auto& info{stage_infos[0]};
445 if (info.uses_render_area) {
446 render_area.uses_render_area = true;
447 render_area.words = {static_cast<float>(regs.surface_clip.width),
448 static_cast<float>(regs.surface_clip.height)};
449 }
442 }}; 450 }};
443 if constexpr (Spec::enabled_stages[0]) { 451 if constexpr (Spec::enabled_stages[0]) {
444 prepare_stage(0); 452 prepare_stage(0);
@@ -455,10 +463,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
455 if constexpr (Spec::enabled_stages[4]) { 463 if constexpr (Spec::enabled_stages[4]) {
456 prepare_stage(4); 464 prepare_stage(4);
457 } 465 }
458 ConfigureDraw(rescaling); 466 ConfigureDraw(rescaling, render_area);
459} 467}
460 468
461void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { 469void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
470 const RenderAreaPushConstant& render_area) {
462 texture_cache.UpdateRenderTargets(false); 471 texture_cache.UpdateRenderTargets(false);
463 scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); 472 scheduler.RequestRenderpass(texture_cache.GetFramebuffer());
464 473
@@ -474,7 +483,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
474 const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; 483 const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)};
475 const void* const descriptor_data{update_descriptor_queue.UpdateData()}; 484 const void* const descriptor_data{update_descriptor_queue.UpdateData()};
476 scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), 485 scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(),
477 is_rescaling, update_rescaling](vk::CommandBuffer cmdbuf) { 486 is_rescaling, update_rescaling,
487 uses_render_area = render_area.uses_render_area,
488 render_area_data = render_area.words](vk::CommandBuffer cmdbuf) {
478 if (bind_pipeline) { 489 if (bind_pipeline) {
479 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 490 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
480 } 491 }
@@ -488,6 +499,11 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
488 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor), 499 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor),
489 &scale_down_factor); 500 &scale_down_factor);
490 } 501 }
502 if (uses_render_area) {
503 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
504 RENDERAREA_LAYOUT_OFFSET, sizeof(render_area_data),
505 &render_area_data);
506 }
491 if (!descriptor_set_layout) { 507 if (!descriptor_set_layout) {
492 return; 508 return;
493 } 509 }
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;
62class PipelineStatistics; 62class PipelineStatistics;
63class RenderPassCache; 63class RenderPassCache;
64class RescalingPushConstant; 64class RescalingPushConstant;
65class RenderAreaPushConstant;
65class Scheduler; 66class Scheduler;
66class UpdateDescriptorQueue; 67class UpdateDescriptorQueue;
67 68
@@ -119,7 +120,8 @@ private:
119 template <typename Spec> 120 template <typename Spec>
120 void ConfigureImpl(bool is_indexed); 121 void ConfigureImpl(bool is_indexed);
121 122
122 void ConfigureDraw(const RescalingPushConstant& rescaling); 123 void ConfigureDraw(const RescalingPushConstant& rescaling,
124 const RenderAreaPushConstant& render_are);
123 125
124 void MakePipeline(VkRenderPass render_pass); 126 void MakePipeline(VkRenderPass render_pass);
125 127
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;
53using VideoCommon::GenericEnvironment; 53using VideoCommon::GenericEnvironment;
54using VideoCommon::GraphicsEnvironment; 54using VideoCommon::GraphicsEnvironment;
55 55
56constexpr u32 CACHE_VERSION = 6; 56constexpr u32 CACHE_VERSION = 7;
57 57
58template <typename Container> 58template <typename Container>
59auto MakeSpan(Container& container) { 59auto 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
683 if (!state_tracker.TouchViewports()) { 683 if (!state_tracker.TouchViewports()) {
684 return; 684 return;
685 } 685 }
686 if (!regs.viewport_scale_offset_enbled) {
687 const auto x = static_cast<float>(regs.surface_clip.x);
688 const auto y = static_cast<float>(regs.surface_clip.y);
689 const auto width = static_cast<float>(regs.surface_clip.width);
690 const auto height = static_cast<float>(regs.surface_clip.height);
691 VkViewport viewport{
692 .x = x,
693 .y = y,
694 .width = width != 0.0f ? width : 1.0f,
695 .height = height != 0.0f ? height : 1.0f,
696 .minDepth = 0.0f,
697 .maxDepth = 1.0f,
698 };
699 scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); });
700 return;
701 }
686 const bool is_rescaling{texture_cache.IsRescaling()}; 702 const bool is_rescaling{texture_cache.IsRescaling()};
687 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 703 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
688 const std::array viewports{ 704 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 @@
19#include "video_core/engines/kepler_compute.h" 19#include "video_core/engines/kepler_compute.h"
20#include "video_core/memory_manager.h" 20#include "video_core/memory_manager.h"
21#include "video_core/shader_environment.h" 21#include "video_core/shader_environment.h"
22#include "video_core/texture_cache/format_lookup_table.h"
22#include "video_core/textures/texture.h" 23#include "video_core/textures/texture.h"
23 24
24namespace VideoCommon { 25namespace VideoCommon {
@@ -33,7 +34,7 @@ static u64 MakeCbufKey(u32 index, u32 offset) {
33 return (static_cast<u64>(index) << 32) | offset; 34 return (static_cast<u64>(index) << 32) | offset;
34} 35}
35 36
36static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { 37static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& entry) {
37 switch (entry.texture_type) { 38 switch (entry.texture_type) {
38 case Tegra::Texture::TextureType::Texture1D: 39 case Tegra::Texture::TextureType::Texture1D:
39 return Shader::TextureType::Color1D; 40 return Shader::TextureType::Color1D;
@@ -59,6 +60,26 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
59 } 60 }
60} 61}
61 62
63static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
64 switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
65 entry.a_type, entry.srgb_conversion)) {
66 case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM:
67 return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
68 case VideoCore::Surface::PixelFormat::R8_SNORM:
69 return Shader::TexturePixelFormat::R8_SNORM;
70 case VideoCore::Surface::PixelFormat::R8G8_SNORM:
71 return Shader::TexturePixelFormat::R8G8_SNORM;
72 case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
73 return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
74 case VideoCore::Surface::PixelFormat::R16G16_SNORM:
75 return Shader::TexturePixelFormat::R16G16_SNORM;
76 case VideoCore::Surface::PixelFormat::R16_SNORM:
77 return Shader::TexturePixelFormat::R16_SNORM;
78 default:
79 return Shader::TexturePixelFormat::OTHER;
80 }
81}
82
62static std::string_view StageToPrefix(Shader::Stage stage) { 83static std::string_view StageToPrefix(Shader::Stage stage) {
63 switch (stage) { 84 switch (stage) {
64 case Shader::Stage::VertexB: 85 case Shader::Stage::VertexB:
@@ -178,22 +199,31 @@ void GenericEnvironment::Dump(u64 hash) {
178void GenericEnvironment::Serialize(std::ofstream& file) const { 199void GenericEnvironment::Serialize(std::ofstream& file) const {
179 const u64 code_size{static_cast<u64>(CachedSize())}; 200 const u64 code_size{static_cast<u64>(CachedSize())};
180 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 201 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
202 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
181 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; 203 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
182 204
183 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) 205 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
184 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) 206 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
207 .write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
208 sizeof(num_texture_pixel_formats))
185 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 209 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
186 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) 210 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size))
187 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) 211 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
188 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) 212 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
189 .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest)) 213 .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest))
190 .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest)) 214 .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest))
215 .write(reinterpret_cast<const char*>(&viewport_transform_state),
216 sizeof(viewport_transform_state))
191 .write(reinterpret_cast<const char*>(&stage), sizeof(stage)) 217 .write(reinterpret_cast<const char*>(&stage), sizeof(stage))
192 .write(reinterpret_cast<const char*>(code.data()), code_size); 218 .write(reinterpret_cast<const char*>(code.data()), code_size);
193 for (const auto& [key, type] : texture_types) { 219 for (const auto& [key, type] : texture_types) {
194 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 220 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
195 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 221 .write(reinterpret_cast<const char*>(&type), sizeof(type));
196 } 222 }
223 for (const auto& [key, format] : texture_pixel_formats) {
224 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
225 .write(reinterpret_cast<const char*>(&format), sizeof(format));
226 }
197 for (const auto& [key, type] : cbuf_values) { 227 for (const auto& [key, type] : cbuf_values) {
198 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 228 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
199 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 229 .write(reinterpret_cast<const char*>(&type), sizeof(type));
@@ -237,15 +267,13 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
237 return std::nullopt; 267 return std::nullopt;
238} 268}
239 269
240Shader::TextureType GenericEnvironment::ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, 270Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit,
241 bool via_header_index, u32 raw) { 271 bool via_header_index, u32 raw) {
242 const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)}; 272 const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)};
243 const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)}; 273 const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)};
244 Tegra::Texture::TICEntry entry; 274 Tegra::Texture::TICEntry entry;
245 gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry)); 275 gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry));
246 const Shader::TextureType result{ConvertType(entry)}; 276 return entry;
247 texture_types.emplace(raw, result);
248 return result;
249} 277}
250 278
251GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, 279GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -305,8 +333,27 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
305Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 333Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
306 const auto& regs{maxwell3d->regs}; 334 const auto& regs{maxwell3d->regs};
307 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; 335 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
308 return ReadTextureTypeImpl(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, 336 auto entry =
309 handle); 337 ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
338 const Shader::TextureType result{ConvertTextureType(entry)};
339 texture_types.emplace(handle, result);
340 return result;
341}
342
343Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handle) {
344 const auto& regs{maxwell3d->regs};
345 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
346 auto entry =
347 ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
348 const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry));
349 texture_pixel_formats.emplace(handle, result);
350 return result;
351}
352
353u32 GraphicsEnvironment::ReadViewportTransformState() {
354 const auto& regs{maxwell3d->regs};
355 viewport_transform_state = regs.viewport_scale_offset_enbled;
356 return viewport_transform_state;
310} 357}
311 358
312ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, 359ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_,
@@ -337,21 +384,41 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
337Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { 384Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) {
338 const auto& regs{kepler_compute->regs}; 385 const auto& regs{kepler_compute->regs};
339 const auto& qmd{kepler_compute->launch_description}; 386 const auto& qmd{kepler_compute->launch_description};
340 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); 387 auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
388 const Shader::TextureType result{ConvertTextureType(entry)};
389 texture_types.emplace(handle, result);
390 return result;
391}
392
393Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle) {
394 const auto& regs{kepler_compute->regs};
395 const auto& qmd{kepler_compute->launch_description};
396 auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
397 const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry));
398 texture_pixel_formats.emplace(handle, result);
399 return result;
400}
401
402u32 ComputeEnvironment::ReadViewportTransformState() {
403 return viewport_transform_state;
341} 404}
342 405
343void FileEnvironment::Deserialize(std::ifstream& file) { 406void FileEnvironment::Deserialize(std::ifstream& file) {
344 u64 code_size{}; 407 u64 code_size{};
345 u64 num_texture_types{}; 408 u64 num_texture_types{};
409 u64 num_texture_pixel_formats{};
346 u64 num_cbuf_values{}; 410 u64 num_cbuf_values{};
347 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) 411 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
348 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) 412 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
413 .read(reinterpret_cast<char*>(&num_texture_pixel_formats),
414 sizeof(num_texture_pixel_formats))
349 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 415 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
350 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) 416 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size))
351 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) 417 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
352 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) 418 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
353 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest)) 419 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest))
354 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest)) 420 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest))
421 .read(reinterpret_cast<char*>(&viewport_transform_state), sizeof(viewport_transform_state))
355 .read(reinterpret_cast<char*>(&stage), sizeof(stage)); 422 .read(reinterpret_cast<char*>(&stage), sizeof(stage));
356 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64))); 423 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64)));
357 file.read(reinterpret_cast<char*>(code.get()), code_size); 424 file.read(reinterpret_cast<char*>(code.get()), code_size);
@@ -362,6 +429,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
362 .read(reinterpret_cast<char*>(&type), sizeof(type)); 429 .read(reinterpret_cast<char*>(&type), sizeof(type));
363 texture_types.emplace(key, type); 430 texture_types.emplace(key, type);
364 } 431 }
432 for (size_t i = 0; i < num_texture_pixel_formats; ++i) {
433 u32 key;
434 Shader::TexturePixelFormat format;
435 file.read(reinterpret_cast<char*>(&key), sizeof(key))
436 .read(reinterpret_cast<char*>(&format), sizeof(format));
437 texture_pixel_formats.emplace(key, format);
438 }
365 for (size_t i = 0; i < num_cbuf_values; ++i) { 439 for (size_t i = 0; i < num_cbuf_values; ++i) {
366 u64 key; 440 u64 key;
367 u32 value; 441 u32 value;
@@ -409,6 +483,18 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) {
409 return it->second; 483 return it->second;
410} 484}
411 485
486Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
487 const auto it{texture_pixel_formats.find(handle)};
488 if (it == texture_pixel_formats.end()) {
489 throw Shader::LogicError("Uncached read texture pixel format");
490 }
491 return it->second;
492}
493
494u32 FileEnvironment::ReadViewportTransformState() {
495 return viewport_transform_state;
496}
497
412u32 FileEnvironment::LocalMemorySize() const { 498u32 FileEnvironment::LocalMemorySize() const {
413 return local_memory_size; 499 return local_memory_size;
414} 500}
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:
63protected: 63protected:
64 std::optional<u64> TryFindSize(); 64 std::optional<u64> TryFindSize();
65 65
66 Shader::TextureType ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, bool via_header_index, 66 Tegra::Texture::TICEntry ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit,
67 u32 raw); 67 bool via_header_index, u32 raw);
68 68
69 Tegra::MemoryManager* gpu_memory{}; 69 Tegra::MemoryManager* gpu_memory{};
70 GPUVAddr program_base{}; 70 GPUVAddr program_base{};
71 71
72 std::vector<u64> code; 72 std::vector<u64> code;
73 std::unordered_map<u32, Shader::TextureType> texture_types; 73 std::unordered_map<u32, Shader::TextureType> texture_types;
74 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
74 std::unordered_map<u64, u32> cbuf_values; 75 std::unordered_map<u64, u32> cbuf_values;
75 76
76 u32 local_memory_size{}; 77 u32 local_memory_size{};
@@ -85,6 +86,8 @@ protected:
85 u32 cached_highest = 0; 86 u32 cached_highest = 0;
86 u32 initial_offset = 0; 87 u32 initial_offset = 0;
87 88
89 u32 viewport_transform_state = 1;
90
88 bool has_unbound_instructions = false; 91 bool has_unbound_instructions = false;
89}; 92};
90 93
@@ -102,6 +105,10 @@ public:
102 105
103 Shader::TextureType ReadTextureType(u32 handle) override; 106 Shader::TextureType ReadTextureType(u32 handle) override;
104 107
108 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
109
110 u32 ReadViewportTransformState() override;
111
105private: 112private:
106 Tegra::Engines::Maxwell3D* maxwell3d{}; 113 Tegra::Engines::Maxwell3D* maxwell3d{};
107 size_t stage_index{}; 114 size_t stage_index{};
@@ -120,6 +127,10 @@ public:
120 127
121 Shader::TextureType ReadTextureType(u32 handle) override; 128 Shader::TextureType ReadTextureType(u32 handle) override;
122 129
130 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
131
132 u32 ReadViewportTransformState() override;
133
123private: 134private:
124 Tegra::Engines::KeplerCompute* kepler_compute{}; 135 Tegra::Engines::KeplerCompute* kepler_compute{};
125}; 136};
@@ -143,6 +154,10 @@ public:
143 154
144 [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; 155 [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override;
145 156
157 [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
158
159 [[nodiscard]] u32 ReadViewportTransformState() override;
160
146 [[nodiscard]] u32 LocalMemorySize() const override; 161 [[nodiscard]] u32 LocalMemorySize() const override;
147 162
148 [[nodiscard]] u32 SharedMemorySize() const override; 163 [[nodiscard]] u32 SharedMemorySize() const override;
@@ -156,6 +171,7 @@ public:
156private: 171private:
157 std::unique_ptr<u64[]> code; 172 std::unique_ptr<u64[]> code;
158 std::unordered_map<u32, Shader::TextureType> texture_types; 173 std::unordered_map<u32, Shader::TextureType> texture_types;
174 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
159 std::unordered_map<u64, u32> cbuf_values; 175 std::unordered_map<u64, u32> cbuf_values;
160 std::array<u32, 3> workgroup_size{}; 176 std::array<u32, 3> workgroup_size{};
161 u32 local_memory_size{}; 177 u32 local_memory_size{};
@@ -164,6 +180,7 @@ private:
164 u32 read_lowest{}; 180 u32 read_lowest{};
165 u32 read_highest{}; 181 u32 read_highest{};
166 u32 initial_offset{}; 182 u32 initial_offset{};
183 u32 viewport_transform_state = 1;
167}; 184};
168 185
169void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 186void 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
516 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); 516 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size);
517 const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block; 517 const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block;
518 518
519 UNIMPLEMENTED_IF(info.tile_width_spacing > 0);
520 UNIMPLEMENTED_IF(copy.image_offset.x != 0); 519 UNIMPLEMENTED_IF(copy.image_offset.x != 0);
521 UNIMPLEMENTED_IF(copy.image_offset.y != 0); 520 UNIMPLEMENTED_IF(copy.image_offset.y != 0);
522 UNIMPLEMENTED_IF(copy.image_offset.z != 0); 521 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)
315if (NOT WIN32) 315if (NOT WIN32)
316 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 316 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
317endif() 317endif()
318if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 318if (UNIX AND NOT APPLE)
319 target_link_libraries(yuzu PRIVATE Qt::DBus) 319 target_link_libraries(yuzu PRIVATE Qt::DBus)
320endif() 320endif()
321 321
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() {
820 820
821 ReadBasicSetting(UISettings::values.show_add_ons); 821 ReadBasicSetting(UISettings::values.show_add_ons);
822 ReadBasicSetting(UISettings::values.show_compat); 822 ReadBasicSetting(UISettings::values.show_compat);
823 ReadBasicSetting(UISettings::values.show_size);
824 ReadBasicSetting(UISettings::values.show_types);
823 ReadBasicSetting(UISettings::values.game_icon_size); 825 ReadBasicSetting(UISettings::values.game_icon_size);
824 ReadBasicSetting(UISettings::values.folder_icon_size); 826 ReadBasicSetting(UISettings::values.folder_icon_size);
825 ReadBasicSetting(UISettings::values.row_1_text_id); 827 ReadBasicSetting(UISettings::values.row_1_text_id);
@@ -1416,6 +1418,8 @@ void Config::SaveUIGamelistValues() {
1416 1418
1417 WriteBasicSetting(UISettings::values.show_add_ons); 1419 WriteBasicSetting(UISettings::values.show_add_ons);
1418 WriteBasicSetting(UISettings::values.show_compat); 1420 WriteBasicSetting(UISettings::values.show_compat);
1421 WriteBasicSetting(UISettings::values.show_size);
1422 WriteBasicSetting(UISettings::values.show_types);
1419 WriteBasicSetting(UISettings::values.game_icon_size); 1423 WriteBasicSetting(UISettings::values.game_icon_size);
1420 WriteBasicSetting(UISettings::values.folder_icon_size); 1424 WriteBasicSetting(UISettings::values.folder_icon_size);
1421 WriteBasicSetting(UISettings::values.row_1_text_id); 1425 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)
73 // Force game list reload if any of the relevant settings are changed. 73 // Force game list reload if any of the relevant settings are changed.
74 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); 74 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
75 connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); 75 connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
76 connect(ui->show_size, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
77 connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
76 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 78 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
77 &ConfigureUi::RequestGameListUpdate); 79 &ConfigureUi::RequestGameListUpdate);
78 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), 80 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
@@ -111,6 +113,8 @@ void ConfigureUi::ApplyConfiguration() {
111 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 113 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
112 UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); 114 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
113 UISettings::values.show_compat = ui->show_compat->isChecked(); 115 UISettings::values.show_compat = ui->show_compat->isChecked();
116 UISettings::values.show_size = ui->show_size->isChecked();
117 UISettings::values.show_types = ui->show_types->isChecked();
114 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); 118 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
115 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); 119 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
116 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 120 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
@@ -132,6 +136,8 @@ void ConfigureUi::SetConfiguration() {
132 ui->language_combobox->findData(UISettings::values.language)); 136 ui->language_combobox->findData(UISettings::values.language));
133 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 137 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
134 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); 138 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
139 ui->show_size->setChecked(UISettings::values.show_size.GetValue());
140 ui->show_types->setChecked(UISettings::values.show_types.GetValue());
135 ui->game_icon_size_combobox->setCurrentIndex( 141 ui->game_icon_size_combobox->setCurrentIndex(
136 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); 142 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
137 ui->folder_icon_size_combobox->setCurrentIndex( 143 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 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>363</width> 9 <width>363</width>
10 <height>507</height> 10 <height>562</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -91,6 +91,20 @@
91 </widget> 91 </widget>
92 </item> 92 </item>
93 <item> 93 <item>
94 <widget class="QCheckBox" name="show_size">
95 <property name="text">
96 <string>Show Size Column</string>
97 </property>
98 </widget>
99 </item>
100 <item>
101 <widget class="QCheckBox" name="show_types">
102 <property name="text">
103 <string>Show File Types Column</string>
104 </property>
105 </widget>
106 </item>
107 <item>
94 <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2"> 108 <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2">
95 <item> 109 <item>
96 <widget class="QLabel" name="game_icon_size_label"> 110 <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) {
788 // Update the columns in case UISettings has changed 788 // Update the columns in case UISettings has changed
789 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); 789 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
790 tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); 790 tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
791 tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types);
792 tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
791 793
792 // Delete any rows that might already exist if we're repopulating 794 // Delete any rows that might already exist if we're repopulating
793 item_model->removeRows(0, item_model->rowCount()); 795 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 @@
9#ifdef __APPLE__ 9#ifdef __APPLE__
10#include <unistd.h> // for chdir 10#include <unistd.h> // for chdir
11#endif 11#endif
12#ifdef __linux__ 12#ifdef __unix__
13#include <csignal> 13#include <csignal>
14#include <sys/socket.h> 14#include <sys/socket.h>
15#endif 15#endif
@@ -275,7 +275,7 @@ static void OverrideWindowsFont() {
275#endif 275#endif
276 276
277bool GMainWindow::CheckDarkMode() { 277bool GMainWindow::CheckDarkMode() {
278#ifdef __linux__ 278#ifdef __unix__
279 const QPalette test_palette(qApp->palette()); 279 const QPalette test_palette(qApp->palette());
280 const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text); 280 const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text);
281 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); 281 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
@@ -283,7 +283,7 @@ bool GMainWindow::CheckDarkMode() {
283#else 283#else
284 // TODO: Windows 284 // TODO: Windows
285 return false; 285 return false;
286#endif // __linux__ 286#endif // __unix__
287} 287}
288 288
289GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) 289GMainWindow::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
291 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, 291 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
292 vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 292 vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
293 provider{std::make_unique<FileSys::ManualContentProvider>()} { 293 provider{std::make_unique<FileSys::ManualContentProvider>()} {
294#ifdef __linux__ 294#ifdef __unix__
295 SetupSigInterrupts(); 295 SetupSigInterrupts();
296#endif 296#endif
297 system->Initialize(); 297 system->Initialize();
@@ -509,7 +509,7 @@ GMainWindow::~GMainWindow() {
509 delete render_window; 509 delete render_window;
510 } 510 }
511 511
512#ifdef __linux__ 512#ifdef __unix__
513 ::close(sig_interrupt_fds[0]); 513 ::close(sig_interrupt_fds[0]);
514 ::close(sig_interrupt_fds[1]); 514 ::close(sig_interrupt_fds[1]);
515#endif 515#endif
@@ -1379,7 +1379,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
1379} 1379}
1380 1380
1381void GMainWindow::SetupPrepareForSleep() { 1381void GMainWindow::SetupPrepareForSleep() {
1382#ifdef __linux__ 1382#ifdef __unix__
1383 auto bus = QDBusConnection::systemBus(); 1383 auto bus = QDBusConnection::systemBus();
1384 if (bus.isConnected()) { 1384 if (bus.isConnected()) {
1385 const bool success = bus.connect( 1385 const bool success = bus.connect(
@@ -1393,7 +1393,7 @@ void GMainWindow::SetupPrepareForSleep() {
1393 } else { 1393 } else {
1394 LOG_WARNING(Frontend, "QDBusConnection system bus is not connected"); 1394 LOG_WARNING(Frontend, "QDBusConnection system bus is not connected");
1395 } 1395 }
1396#endif // __linux__ 1396#endif // __unix__
1397} 1397}
1398 1398
1399void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { 1399void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
@@ -1415,7 +1415,7 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
1415 } 1415 }
1416} 1416}
1417 1417
1418#ifdef __linux__ 1418#ifdef __unix__
1419static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) { 1419static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
1420 if (!QDBusConnection::sessionBus().isConnected()) { 1420 if (!QDBusConnection::sessionBus().isConnected()) {
1421 return {}; 1421 return {};
@@ -1500,14 +1500,14 @@ void GMainWindow::OnSigInterruptNotifierActivated() {
1500 1500
1501 emit SigInterrupt(); 1501 emit SigInterrupt();
1502} 1502}
1503#endif // __linux__ 1503#endif // __unix__
1504 1504
1505void GMainWindow::PreventOSSleep() { 1505void GMainWindow::PreventOSSleep() {
1506#ifdef _WIN32 1506#ifdef _WIN32
1507 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 1507 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
1508#elif defined(HAVE_SDL2) 1508#elif defined(HAVE_SDL2)
1509 SDL_DisableScreenSaver(); 1509 SDL_DisableScreenSaver();
1510#ifdef __linux__ 1510#ifdef __unix__
1511 auto reply = HoldWakeLockLinux(winId()); 1511 auto reply = HoldWakeLockLinux(winId());
1512 if (reply) { 1512 if (reply) {
1513 wake_lock = std::move(reply.value()); 1513 wake_lock = std::move(reply.value());
@@ -1521,7 +1521,7 @@ void GMainWindow::AllowOSSleep() {
1521 SetThreadExecutionState(ES_CONTINUOUS); 1521 SetThreadExecutionState(ES_CONTINUOUS);
1522#elif defined(HAVE_SDL2) 1522#elif defined(HAVE_SDL2)
1523 SDL_EnableScreenSaver(); 1523 SDL_EnableScreenSaver();
1524#ifdef __linux__ 1524#ifdef __unix__
1525 if (!wake_lock.path().isEmpty()) { 1525 if (!wake_lock.path().isEmpty()) {
1526 ReleaseWakeLockLinux(wake_lock); 1526 ReleaseWakeLockLinux(wake_lock);
1527 } 1527 }
@@ -4070,7 +4070,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
4070} 4070}
4071 4071
4072void GMainWindow::changeEvent(QEvent* event) { 4072void GMainWindow::changeEvent(QEvent* event) {
4073#ifdef __linux__ 4073#ifdef __unix__
4074 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to 4074 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to
4075 // UpdateUITheme is a decent work around 4075 // UpdateUITheme is a decent work around
4076 if (event->type() == QEvent::PaletteChange) { 4076 if (event->type() == QEvent::PaletteChange) {
@@ -4085,7 +4085,7 @@ void GMainWindow::changeEvent(QEvent* event) {
4085 } 4085 }
4086 last_window_color = window_color; 4086 last_window_color = window_color;
4087 } 4087 }
4088#endif // __linux__ 4088#endif // __unix__
4089 QWidget::changeEvent(event); 4089 QWidget::changeEvent(event);
4090} 4090}
4091 4091
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 @@
15#include "yuzu/compatibility_list.h" 15#include "yuzu/compatibility_list.h"
16#include "yuzu/hotkeys.h" 16#include "yuzu/hotkeys.h"
17 17
18#ifdef __linux__ 18#ifdef __unix__
19#include <QVariant> 19#include <QVariant>
20#include <QtDBus/QDBusInterface> 20#include <QtDBus/QDBusInterface>
21#include <QtDBus/QtDBus> 21#include <QtDBus/QtDBus>
@@ -255,7 +255,7 @@ private:
255 void changeEvent(QEvent* event) override; 255 void changeEvent(QEvent* event) override;
256 void closeEvent(QCloseEvent* event) override; 256 void closeEvent(QCloseEvent* event) override;
257 257
258#ifdef __linux__ 258#ifdef __unix__
259 void SetupSigInterrupts(); 259 void SetupSigInterrupts();
260 static void HandleSigInterrupt(int); 260 static void HandleSigInterrupt(int);
261 void OnSigInterruptNotifierActivated(); 261 void OnSigInterruptNotifierActivated();
@@ -435,7 +435,7 @@ private:
435 // True if TAS recording dialog is visible 435 // True if TAS recording dialog is visible
436 bool is_tas_recording_dialog_active{}; 436 bool is_tas_recording_dialog_active{};
437 437
438#ifdef __linux__ 438#ifdef __unix__
439 QSocketNotifier* sig_interrupt_notifier; 439 QSocketNotifier* sig_interrupt_notifier;
440 static std::array<int, 3> sig_interrupt_fds; 440 static std::array<int, 3> sig_interrupt_fds;
441 441
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 {
132 // Compatibility List 132 // Compatibility List
133 Settings::Setting<bool> show_compat{false, "show_compat"}; 133 Settings::Setting<bool> show_compat{false, "show_compat"};
134 134
135 // Size & File Types Column
136 Settings::Setting<bool> show_size{true, "show_size"};
137 Settings::Setting<bool> show_types{true, "show_types"};
138
135 bool configuration_applied; 139 bool configuration_applied;
136 bool reset_to_defaults; 140 bool reset_to_defaults;
137 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; 141 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};