summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/boost0
-rw-r--r--src/core/hle/ipc.h3
-rw-r--r--src/core/hle/ipc_helpers.h149
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp99
-rw-r--r--src/core/hle/kernel/hle_ipc.h54
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp10
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h7
-rw-r--r--src/core/hle/service/nwm/uds_beacon.h3
-rw-r--r--src/core/hle/service/service.cpp19
-rw-r--r--src/core/hle/service/sm/srv.cpp105
-rw-r--r--src/video_core/regs_texturing.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp19
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h13
-rw-r--r--src/video_core/swrasterizer/rasterizer.cpp20
-rw-r--r--src/video_core/swrasterizer/texturing.cpp19
15 files changed, 434 insertions, 94 deletions
diff --git a/externals/boost b/externals/boost
Subproject 351972396392c97a659b9a02f34ce9269293d21 Subproject 3abc84abaf63a068cb59a9f9b5675c1947bc6fd
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 303ca090d..f7f96125a 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -44,6 +44,9 @@ inline u32* GetStaticBuffers(const int offset = 0) {
44 44
45namespace IPC { 45namespace IPC {
46 46
47/// Size of the command buffer area, in 32-bit words.
48constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
49
47// These errors are commonly returned by invalid IPC translations, so alias them here for 50// These errors are commonly returned by invalid IPC translations, so alias them here for
48// convenience. 51// convenience.
49// TODO(yuriks): These will probably go away once translation is implemented inside the kernel. 52// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index d7348c09d..f0d89cffe 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -4,19 +4,28 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <tuple>
9#include <type_traits>
10#include <utility>
7#include "core/hle/ipc.h" 11#include "core/hle/ipc.h"
8#include "core/hle/kernel/handle_table.h" 12#include "core/hle/kernel/handle_table.h"
13#include "core/hle/kernel/hle_ipc.h"
9#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
10 15
11namespace IPC { 16namespace IPC {
12 17
13class RequestHelperBase { 18class RequestHelperBase {
14protected: 19protected:
20 Kernel::HLERequestContext* context = nullptr;
15 u32* cmdbuf; 21 u32* cmdbuf;
16 ptrdiff_t index = 1; 22 ptrdiff_t index = 1;
17 Header header; 23 Header header;
18 24
19public: 25public:
26 RequestHelperBase(Kernel::HLERequestContext& context, Header desired_header)
27 : context(&context), cmdbuf(context.CommandBuffer()), header(desired_header) {}
28
20 RequestHelperBase(u32* command_buffer, Header command_header) 29 RequestHelperBase(u32* command_buffer, Header command_header)
21 : cmdbuf(command_buffer), header(command_header) {} 30 : cmdbuf(command_buffer), header(command_header) {}
22 31
@@ -51,12 +60,27 @@ public:
51 60
52class RequestBuilder : public RequestHelperBase { 61class RequestBuilder : public RequestHelperBase {
53public: 62public:
63 RequestBuilder(Kernel::HLERequestContext& context, Header command_header)
64 : RequestHelperBase(context, command_header) {
65 // From this point we will start overwriting the existing command buffer, so it's safe to
66 // release all previous incoming Object pointers since they won't be usable anymore.
67 context.ClearIncomingObjects();
68 cmdbuf[0] = header.raw;
69 }
70
71 RequestBuilder(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size,
72 unsigned translate_params_size)
73 : RequestBuilder(
74 context, Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {}
75
54 RequestBuilder(u32* command_buffer, Header command_header) 76 RequestBuilder(u32* command_buffer, Header command_header)
55 : RequestHelperBase(command_buffer, command_header) { 77 : RequestHelperBase(command_buffer, command_header) {
56 cmdbuf[0] = header.raw; 78 cmdbuf[0] = header.raw;
57 } 79 }
80
58 explicit RequestBuilder(u32* command_buffer, u32 command_header) 81 explicit RequestBuilder(u32* command_buffer, u32 command_header)
59 : RequestBuilder(command_buffer, Header{command_header}) {} 82 : RequestBuilder(command_buffer, Header{command_header}) {}
83
60 RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size, 84 RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size,
61 unsigned translate_params_size) 85 unsigned translate_params_size)
62 : RequestBuilder(command_buffer, 86 : RequestBuilder(command_buffer,
@@ -88,6 +112,9 @@ public:
88 template <typename... H> 112 template <typename... H>
89 void PushMoveHandles(H... handles); 113 void PushMoveHandles(H... handles);
90 114
115 template <typename... O>
116 void PushObjects(Kernel::SharedPtr<O>... pointers);
117
91 void PushCurrentPIDHandle(); 118 void PushCurrentPIDHandle();
92 119
93 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); 120 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id);
@@ -153,6 +180,11 @@ inline void RequestBuilder::PushMoveHandles(H... handles) {
153 Push(static_cast<Kernel::Handle>(handles)...); 180 Push(static_cast<Kernel::Handle>(handles)...);
154} 181}
155 182
183template <typename... O>
184inline void RequestBuilder::PushObjects(Kernel::SharedPtr<O>... pointers) {
185 PushMoveHandles(context->AddOutgoingHandle(std::move(pointers))...);
186}
187
156inline void RequestBuilder::PushCurrentPIDHandle() { 188inline void RequestBuilder::PushCurrentPIDHandle() {
157 Push(CallingPidDesc()); 189 Push(CallingPidDesc());
158 Push(u32(0)); 190 Push(u32(0));
@@ -171,10 +203,21 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size,
171 203
172class RequestParser : public RequestHelperBase { 204class RequestParser : public RequestHelperBase {
173public: 205public:
206 RequestParser(Kernel::HLERequestContext& context, Header desired_header)
207 : RequestHelperBase(context, desired_header) {}
208
209 RequestParser(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size,
210 unsigned translate_params_size)
211 : RequestParser(context,
212 Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {
213 }
214
174 RequestParser(u32* command_buffer, Header command_header) 215 RequestParser(u32* command_buffer, Header command_header)
175 : RequestHelperBase(command_buffer, command_header) {} 216 : RequestHelperBase(command_buffer, command_header) {}
217
176 explicit RequestParser(u32* command_buffer, u32 command_header) 218 explicit RequestParser(u32* command_buffer, u32 command_header)
177 : RequestParser(command_buffer, Header{command_header}) {} 219 : RequestParser(command_buffer, Header{command_header}) {}
220
178 RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size, 221 RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size,
179 unsigned translate_params_size) 222 unsigned translate_params_size)
180 : RequestParser(command_buffer, 223 : RequestParser(command_buffer,
@@ -186,7 +229,10 @@ public:
186 ValidateHeader(); 229 ValidateHeader();
187 Header builderHeader{ 230 Header builderHeader{
188 MakeHeader(header.command_id, normal_params_size, translate_params_size)}; 231 MakeHeader(header.command_id, normal_params_size, translate_params_size)};
189 return {cmdbuf, builderHeader}; 232 if (context != nullptr)
233 return {*context, builderHeader};
234 else
235 return {cmdbuf, builderHeader};
190 } 236 }
191 237
192 template <typename T> 238 template <typename T>
@@ -198,10 +244,52 @@ public:
198 template <typename First, typename... Other> 244 template <typename First, typename... Other>
199 void Pop(First& first_value, Other&... other_values); 245 void Pop(First& first_value, Other&... other_values);
200 246
247 /// Equivalent to calling `PopHandles<1>()[0]`.
201 Kernel::Handle PopHandle(); 248 Kernel::Handle PopHandle();
202 249
250 /**
251 * Pops a descriptor containing `N` handles. The handles are returned as an array. The
252 * descriptor must contain exactly `N` handles, it is not permitted to, for example, call
253 * PopHandles<1>() twice to read a multi-handle descriptor with 2 handles, or to make a single
254 * PopHandles<2>() call to read 2 single-handle descriptors.
255 */
256 template <unsigned int N>
257 std::array<Kernel::Handle, N> PopHandles();
258
259 /// Convenience wrapper around PopHandles() which assigns the handles to the passed references.
203 template <typename... H> 260 template <typename... H>
204 void PopHandles(H&... handles); 261 void PopHandles(H&... handles) {
262 std::tie(handles...) = PopHandles<sizeof...(H)>();
263 }
264
265 /// Equivalent to calling `PopGenericObjects<1>()[0]`.
266 Kernel::SharedPtr<Kernel::Object> PopGenericObject();
267
268 /// Equivalent to calling `std::get<0>(PopObjects<T>())`.
269 template <typename T>
270 Kernel::SharedPtr<T> PopObject();
271
272 /**
273 * Pop a descriptor containing `N` handles and resolves them to Kernel::Object pointers. If a
274 * handle is invalid, null is returned for that object instead. The same caveats from
275 * PopHandles() apply regarding `N` matching the number of handles in the descriptor.
276 */
277 template <unsigned int N>
278 std::array<Kernel::SharedPtr<Kernel::Object>, N> PopGenericObjects();
279
280 /**
281 * Resolves handles to Kernel::Objects as in PopGenericsObjects(), but then also casts them to
282 * the passed `T` types, while verifying that the cast is valid. If the type of an object does
283 * not match, null is returned instead.
284 */
285 template <typename... T>
286 std::tuple<Kernel::SharedPtr<T>...> PopObjects();
287
288 /// Convenience wrapper around PopObjects() which assigns the handles to the passed references.
289 template <typename... T>
290 void PopObjects(Kernel::SharedPtr<T>&... pointers) {
291 std::tie(pointers...) = PopObjects<T...>();
292 }
205 293
206 /** 294 /**
207 * @brief Pops the static buffer vaddr 295 * @brief Pops the static buffer vaddr
@@ -313,15 +401,54 @@ inline Kernel::Handle RequestParser::PopHandle() {
313 return Pop<Kernel::Handle>(); 401 return Pop<Kernel::Handle>();
314} 402}
315 403
316template <typename... H> 404template <unsigned int N>
317void RequestParser::PopHandles(H&... handles) { 405std::array<Kernel::Handle, N> RequestParser::PopHandles() {
318 const u32 handle_descriptor = Pop<u32>(); 406 u32 handle_descriptor = Pop<u32>();
319 const int handles_number = sizeof...(H); 407 ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
320 DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), 408 "Tried to pop handle(s) but the descriptor is not a handle descriptor");
321 "Tried to pop handle(s) but the descriptor is not a handle descriptor"); 409 ASSERT_MSG(N == HandleNumberFromDesc(handle_descriptor),
322 DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor), 410 "Number of handles doesn't match the descriptor");
323 "Number of handles doesn't match the descriptor"); 411
324 Pop(static_cast<Kernel::Handle&>(handles)...); 412 std::array<Kernel::Handle, N> handles{};
413 for (Kernel::Handle& handle : handles) {
414 handle = Pop<Kernel::Handle>();
415 }
416 return handles;
417}
418
419inline Kernel::SharedPtr<Kernel::Object> RequestParser::PopGenericObject() {
420 Kernel::Handle handle = PopHandle();
421 return context->GetIncomingHandle(handle);
422}
423
424template <typename T>
425Kernel::SharedPtr<T> RequestParser::PopObject() {
426 return Kernel::DynamicObjectCast<T>(PopGenericObject());
427}
428
429template <unsigned int N>
430inline std::array<Kernel::SharedPtr<Kernel::Object>, N> RequestParser::PopGenericObjects() {
431 std::array<Kernel::Handle, N> handles = PopHandles<N>();
432 std::array<Kernel::SharedPtr<Kernel::Object>, N> pointers;
433 for (int i = 0; i < N; ++i) {
434 pointers[i] = context->GetIncomingHandle(handles[i]);
435 }
436 return pointers;
437}
438
439namespace detail {
440template <typename... T, size_t... I>
441std::tuple<Kernel::SharedPtr<T>...> PopObjectsHelper(
442 std::array<Kernel::SharedPtr<Kernel::Object>, sizeof...(T)>&& pointers,
443 std::index_sequence<I...>) {
444 return std::make_tuple(Kernel::DynamicObjectCast<T>(std::move(pointers[I]))...);
445}
446} // namespace detail
447
448template <typename... T>
449inline std::tuple<Kernel::SharedPtr<T>...> RequestParser::PopObjects() {
450 return detail::PopObjectsHelper<T...>(PopGenericObjects<sizeof...(T)>(),
451 std::index_sequence_for<T...>{});
325} 452}
326 453
327inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) { 454inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) {
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index a60b8ef00..6cf1886cf 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -5,8 +5,10 @@
5#include <boost/range/algorithm_ext/erase.hpp> 5#include <boost/range/algorithm_ext/erase.hpp>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/handle_table.h"
8#include "core/hle/kernel/hle_ipc.h" 9#include "core/hle/kernel/hle_ipc.h"
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/process.h"
10#include "core/hle/kernel/server_session.h" 12#include "core/hle/kernel/server_session.h"
11 13
12namespace Kernel { 14namespace Kernel {
@@ -23,4 +25,101 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
23 25
24HLERequestContext::~HLERequestContext() = default; 26HLERequestContext::~HLERequestContext() = default;
25 27
28SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const {
29 ASSERT(id_from_cmdbuf < request_handles.size());
30 return request_handles[id_from_cmdbuf];
31}
32
33u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) {
34 request_handles.push_back(std::move(object));
35 return request_handles.size() - 1;
36}
37
38void HLERequestContext::ClearIncomingObjects() {
39 request_handles.clear();
40}
41
42ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
43 Process& src_process,
44 HandleTable& src_table) {
45 IPC::Header header{src_cmdbuf[0]};
46
47 size_t untranslated_size = 1u + header.normal_params_size;
48 size_t command_size = untranslated_size + header.translate_params_size;
49 ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error
50
51 std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin());
52
53 size_t i = untranslated_size;
54 while (i < command_size) {
55 u32 descriptor = cmd_buf[i] = src_cmdbuf[i];
56 i += 1;
57
58 switch (IPC::GetDescriptorType(descriptor)) {
59 case IPC::DescriptorType::CopyHandle:
60 case IPC::DescriptorType::MoveHandle: {
61 u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
62 ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error
63 for (u32 j = 0; j < num_handles; ++j) {
64 Handle handle = src_cmdbuf[i];
65 SharedPtr<Object> object = src_table.GetGeneric(handle);
66 ASSERT(object != nullptr); // TODO(yuriks): Return error
67 if (descriptor == IPC::DescriptorType::MoveHandle) {
68 src_table.Close(handle);
69 }
70
71 cmd_buf[i++] = AddOutgoingHandle(std::move(object));
72 }
73 break;
74 }
75 case IPC::DescriptorType::CallingPid: {
76 cmd_buf[i++] = src_process.process_id;
77 break;
78 }
79 default:
80 UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
81 }
82 }
83
84 return RESULT_SUCCESS;
85}
86
87ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
88 HandleTable& dst_table) const {
89 IPC::Header header{cmd_buf[0]};
90
91 size_t untranslated_size = 1u + header.normal_params_size;
92 size_t command_size = untranslated_size + header.translate_params_size;
93 ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH);
94
95 std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf);
96
97 size_t i = untranslated_size;
98 while (i < command_size) {
99 u32 descriptor = dst_cmdbuf[i] = cmd_buf[i];
100 i += 1;
101
102 switch (IPC::GetDescriptorType(descriptor)) {
103 case IPC::DescriptorType::CopyHandle:
104 case IPC::DescriptorType::MoveHandle: {
105 // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally
106 u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
107 ASSERT(i + num_handles <= command_size);
108 for (u32 j = 0; j < num_handles; ++j) {
109 SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]);
110
111 // TODO(yuriks): Figure out the proper error handling for if this fails
112 Handle handle = dst_table.Create(object).Unwrap();
113 dst_cmdbuf[i++] = handle;
114 }
115 break;
116 }
117 default:
118 UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
119 }
120 }
121
122 return RESULT_SUCCESS;
123}
124
26} // namespace Kernel 125} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index c30184eab..cbb109d8f 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -4,8 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <memory> 8#include <memory>
8#include <vector> 9#include <vector>
10#include <boost/container/small_vector.hpp>
11#include "common/common_types.h"
12#include "common/swap.h"
13#include "core/hle/ipc.h"
9#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/server_session.h" 15#include "core/hle/kernel/server_session.h"
11 16
@@ -15,6 +20,9 @@ class ServiceFrameworkBase;
15 20
16namespace Kernel { 21namespace Kernel {
17 22
23class HandleTable;
24class Process;
25
18/** 26/**
19 * Interface implemented by HLE Session handlers. 27 * Interface implemented by HLE Session handlers.
20 * This can be provided to a ServerSession in order to hook into several relevant events 28 * This can be provided to a ServerSession in order to hook into several relevant events
@@ -59,14 +67,28 @@ protected:
59 * Class containing information about an in-flight IPC request being handled by an HLE service 67 * Class containing information about an in-flight IPC request being handled by an HLE service
60 * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and 68 * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and
61 * when possible use the APIs in this class to service the request. 69 * when possible use the APIs in this class to service the request.
70 *
71 * HLE handle protocol
72 * ===================
73 *
74 * To avoid needing HLE services to keep a separate handle table, or having to directly modify the
75 * requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel
76 * will decode the incoming handles into object pointers and insert a id in the buffer where the
77 * handle would normally be. The service then calls GetIncomingHandle() with that id to get the
78 * pointer to the object. Similarly, instead of inserting a handle into the command buffer, the
79 * service calls AddOutgoingHandle() and stores the returned id where the handle would normally go.
80 *
81 * The end result is similar to just giving services their own real handle tables, but since these
82 * ids are local to a specific context, it avoids requiring services to manage handles for objects
83 * across multiple calls and ensuring that unneeded handles are cleaned up.
62 */ 84 */
63class HLERequestContext { 85class HLERequestContext {
64public: 86public:
65 ~HLERequestContext(); 87 ~HLERequestContext();
66 88
67 /// Returns a pointer to the IPC command buffer for this request. 89 /// Returns a pointer to the IPC command buffer for this request.
68 u32* CommandBuffer() const { 90 u32* CommandBuffer() {
69 return cmd_buf; 91 return cmd_buf.data();
70 } 92 }
71 93
72 /** 94 /**
@@ -77,11 +99,37 @@ public:
77 return session; 99 return session;
78 } 100 }
79 101
102 /**
103 * Resolves a object id from the request command buffer into a pointer to an object. See the
104 * "HLE handle protocol" section in the class documentation for more details.
105 */
106 SharedPtr<Object> GetIncomingHandle(u32 id_from_cmdbuf) const;
107
108 /**
109 * Adds an outgoing object to the response, returning the id which should be used to reference
110 * it. See the "HLE handle protocol" section in the class documentation for more details.
111 */
112 u32 AddOutgoingHandle(SharedPtr<Object> object);
113
114 /**
115 * Discards all Objects from the context, invalidating all ids. This may be called after reading
116 * out all incoming objects, so that the buffer memory can be re-used for outgoing handles, but
117 * this is not required.
118 */
119 void ClearIncomingObjects();
120
80private: 121private:
81 friend class Service::ServiceFrameworkBase; 122 friend class Service::ServiceFrameworkBase;
82 123
83 u32* cmd_buf = nullptr; 124 ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process,
125 HandleTable& src_table);
126 ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
127 HandleTable& dst_table) const;
128
129 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
84 SharedPtr<ServerSession> session; 130 SharedPtr<ServerSession> session;
131 // TODO(yuriks): Check common usage of this and optimize size accordingly
132 boost::container::small_vector<SharedPtr<Object>, 8> request_handles;
85}; 133};
86 134
87} // namespace Kernel 135} // namespace Kernel
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 6c4600f25..e92900d48 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -215,6 +215,11 @@ static void GetConnectionStatus(Interface* self) {
215 rb.Push(RESULT_SUCCESS); 215 rb.Push(RESULT_SUCCESS);
216 rb.PushRaw(connection_status); 216 rb.PushRaw(connection_status);
217 217
218 // Reset the bitmask of changed nodes after each call to this
219 // function to prevent falsely informing games of outstanding
220 // changes in subsequent calls.
221 connection_status.changed_nodes = 0;
222
218 LOG_DEBUG(Service_NWM, "called"); 223 LOG_DEBUG(Service_NWM, "called");
219} 224}
220 225
@@ -314,8 +319,11 @@ static void BeginHostingNetwork(Interface* self) {
314 // The host is always the first node 319 // The host is always the first node
315 connection_status.network_node_id = 1; 320 connection_status.network_node_id = 1;
316 node_info[0].network_node_id = 1; 321 node_info[0].network_node_id = 1;
322 connection_status.nodes[0] = connection_status.network_node_id;
317 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. 323 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
318 connection_status.node_bitmask |= 1; 324 connection_status.node_bitmask |= 1;
325 // Notify the application that the first node was set.
326 connection_status.changed_nodes |= 1;
319 327
320 // If the game has a preferred channel, use that instead. 328 // If the game has a preferred channel, use that instead.
321 if (network_info.channel != 0) 329 if (network_info.channel != 0)
@@ -352,6 +360,8 @@ static void DestroyNetwork(Interface* self) {
352 // Unschedule the beacon broadcast event. 360 // Unschedule the beacon broadcast event.
353 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 361 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
354 362
363 // TODO(Subv): Check if connection_status is indeed reset after this call.
364 connection_status = {};
355 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); 365 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
356 connection_status_event->Signal(); 366 connection_status_event->Signal();
357 367
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index 29b146569..141f49f9c 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -24,6 +24,9 @@ const double MillisecondsPerTU = 1.024;
24// Interval measured in TU, the default value is 100TU = 102.4ms 24// Interval measured in TU, the default value is 100TU = 102.4ms
25const u16 DefaultBeaconInterval = 100; 25const u16 DefaultBeaconInterval = 100;
26 26
27/// The maximum number of nodes that can exist in an UDS session.
28constexpr u32 UDSMaxNodes = 16;
29
27struct NodeInfo { 30struct NodeInfo {
28 u64_le friend_code_seed; 31 u64_le friend_code_seed;
29 std::array<u16_le, 10> username; 32 std::array<u16_le, 10> username;
@@ -47,8 +50,8 @@ struct ConnectionStatus {
47 u32_le status; 50 u32_le status;
48 INSERT_PADDING_WORDS(1); 51 INSERT_PADDING_WORDS(1);
49 u16_le network_node_id; 52 u16_le network_node_id;
50 INSERT_PADDING_BYTES(2); 53 u16_le changed_nodes;
51 INSERT_PADDING_BYTES(32); 54 u16_le nodes[UDSMaxNodes];
52 u8 total_nodes; 55 u8 total_nodes;
53 u8 max_nodes; 56 u8 max_nodes;
54 u16_le node_bitmask; 57 u16_le node_bitmask;
diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h
index 6df4c4f47..caacf4c6f 100644
--- a/src/core/hle/service/nwm/uds_beacon.h
+++ b/src/core/hle/service/nwm/uds_beacon.h
@@ -15,9 +15,6 @@ namespace Service {
15namespace NWM { 15namespace NWM {
16 16
17using MacAddress = std::array<u8, 6>; 17using MacAddress = std::array<u8, 6>;
18
19/// The maximum number of nodes that can exist in an UDS session.
20constexpr u32 UDSMaxNodes = 16;
21constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; 18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
22 19
23/// Additional block tag ids in the Beacon frames 20/// Additional block tag ids in the Beacon frames
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d34968428..791a65c19 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -2,10 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <fmt/format.h> 6#include <fmt/format.h>
7#include "common/assert.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
7#include "common/string_util.h" 9#include "common/string_util.h"
10#include "core/hle/ipc.h"
8#include "core/hle/kernel/client_port.h" 11#include "core/hle/kernel/client_port.h"
12#include "core/hle/kernel/process.h"
9#include "core/hle/kernel/server_port.h" 13#include "core/hle/kernel/server_port.h"
10#include "core/hle/kernel/server_session.h" 14#include "core/hle/kernel/server_session.h"
11#include "core/hle/service/ac/ac.h" 15#include "core/hle/service/ac/ac.h"
@@ -160,12 +164,6 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
160void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) { 164void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
161 u32* cmd_buf = Kernel::GetCommandBuffer(); 165 u32* cmd_buf = Kernel::GetCommandBuffer();
162 166
163 // TODO(yuriks): The kernel should be the one handling this as part of translation after
164 // everything else is migrated
165 Kernel::HLERequestContext context;
166 context.cmd_buf = cmd_buf;
167 context.session = std::move(server_session);
168
169 u32 header_code = cmd_buf[0]; 167 u32 header_code = cmd_buf[0];
170 auto itr = handlers.find(header_code); 168 auto itr = handlers.find(header_code);
171 const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; 169 const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
@@ -173,9 +171,18 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses
173 return ReportUnimplementedFunction(cmd_buf, info); 171 return ReportUnimplementedFunction(cmd_buf, info);
174 } 172 }
175 173
174 // TODO(yuriks): The kernel should be the one handling this as part of translation after
175 // everything else is migrated
176 Kernel::HLERequestContext context;
177 context.session = std::move(server_session);
178 context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
179 Kernel::g_handle_table);
180
176 LOG_TRACE(Service, "%s", 181 LOG_TRACE(Service, "%s",
177 MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); 182 MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str());
178 handler_invoker(this, info->handler_callback, context); 183 handler_invoker(this, info->handler_callback, context);
184 context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
185 Kernel::g_handle_table);
179} 186}
180 187
181//////////////////////////////////////////////////////////////////////////////////////////////////// 188////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp
index b8b62b068..74a1256e0 100644
--- a/src/core/hle/service/sm/srv.cpp
+++ b/src/core/hle/service/sm/srv.cpp
@@ -7,9 +7,11 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/hle/ipc.h" 9#include "core/hle/ipc.h"
10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/client_port.h" 11#include "core/hle/kernel/client_port.h"
11#include "core/hle/kernel/client_session.h" 12#include "core/hle/kernel/client_session.h"
12#include "core/hle/kernel/handle_table.h" 13#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/hle_ipc.h"
13#include "core/hle/kernel/semaphore.h" 15#include "core/hle/kernel/semaphore.h"
14#include "core/hle/kernel/server_session.h" 16#include "core/hle/kernel/server_session.h"
15#include "core/hle/service/sm/sm.h" 17#include "core/hle/service/sm/sm.h"
@@ -30,15 +32,18 @@ constexpr int MAX_PENDING_NOTIFICATIONS = 16;
30 * 1: ResultCode 32 * 1: ResultCode
31 */ 33 */
32void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { 34void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
33 u32* cmd_buff = ctx.CommandBuffer(); 35 IPC::RequestParser rp(ctx, 0x1, 0, 2);
34 36
35 if (cmd_buff[1] != IPC::CallingPidDesc()) { 37 u32 pid_descriptor = rp.Pop<u32>();
36 cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40 38 if (pid_descriptor != IPC::CallingPidDesc()) {
37 cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; 39 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
40 rb.Push(IPC::ERR_INVALID_BUFFER_DESCRIPTOR);
38 return; 41 return;
39 } 42 }
40 cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040 43 u32 caller_pid = rp.Pop<u32>();
41 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 44
45 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
46 rb.Push(RESULT_SUCCESS);
42 LOG_WARNING(Service_SRV, "(STUBBED) called"); 47 LOG_WARNING(Service_SRV, "(STUBBED) called");
43} 48}
44 49
@@ -53,15 +58,14 @@ void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
53 * 3: Handle to semaphore signaled on process notification 58 * 3: Handle to semaphore signaled on process notification
54 */ 59 */
55void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { 60void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
56 u32* cmd_buff = ctx.CommandBuffer(); 61 IPC::RequestParser rp(ctx, 0x2, 0, 0);
57 62
58 notification_semaphore = 63 notification_semaphore =
59 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); 64 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap();
60 65
61 cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042 66 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
62 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 67 rb.Push(RESULT_SUCCESS);
63 cmd_buff[2] = IPC::CopyHandleDesc(1); 68 rb.PushObjects(notification_semaphore);
64 cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom();
65 LOG_WARNING(Service_SRV, "(STUBBED) called"); 69 LOG_WARNING(Service_SRV, "(STUBBED) called");
66} 70}
67 71
@@ -77,43 +81,49 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
77 * 3: Service handle 81 * 3: Service handle
78 */ 82 */
79void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { 83void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
80 ResultCode res = RESULT_SUCCESS; 84 IPC::RequestParser rp(ctx, 0x5, 4, 0);
81 u32* cmd_buff = ctx.CommandBuffer(); 85 auto name_buf = rp.PopRaw<std::array<char, 8>>();
86 size_t name_len = rp.Pop<u32>();
87 u32 flags = rp.Pop<u32>();
88
89 bool return_port_on_failure = (flags & 1) == 0;
82 90
83 size_t name_len = cmd_buff[3];
84 if (name_len > Service::kMaxPortSize) { 91 if (name_len > Service::kMaxPortSize) {
85 cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw; 92 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
86 LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len, 93 rb.Push(ERR_INVALID_NAME_SIZE);
87 cmd_buff[1]); 94 LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len);
88 return; 95 return;
89 } 96 }
90 std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len); 97 std::string name(name_buf.data(), name_len);
91 bool return_port_on_failure = (cmd_buff[4] & 1) == 0;
92 98
93 // TODO(yuriks): Permission checks go here 99 // TODO(yuriks): Permission checks go here
94 100
95 auto client_port = service_manager->GetServicePort(name); 101 auto client_port = service_manager->GetServicePort(name);
96 if (client_port.Failed()) { 102 if (client_port.Failed()) {
97 cmd_buff[1] = client_port.Code().raw; 103 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
98 LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), 104 rb.Push(client_port.Code());
99 cmd_buff[1]); 105 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(),
106 client_port.Code().raw);
100 return; 107 return;
101 } 108 }
102 109
103 auto session = client_port.Unwrap()->Connect(); 110 auto session = client_port.Unwrap()->Connect();
104 cmd_buff[1] = session.Code().raw;
105 if (session.Succeeded()) { 111 if (session.Succeeded()) {
106 cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom(); 112 LOG_DEBUG(Service_SRV, "called service=%s -> session=%u", name.c_str(),
107 LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(), 113 (*session)->GetObjectId());
108 cmd_buff[3]); 114 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
115 rb.Push(session.Code());
116 rb.PushObjects(session.MoveFrom());
109 } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { 117 } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) {
110 cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw; 118 LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u",
111 cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom(); 119 name.c_str(), (*client_port)->GetObjectId());
112 LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(), 120 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
113 cmd_buff[3]); 121 rb.Push(ERR_MAX_CONNECTIONS_REACHED);
122 rb.PushObjects(client_port.MoveFrom());
114 } else { 123 } else {
115 LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), 124 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code());
116 cmd_buff[1]); 125 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
126 rb.Push(session.Code());
117 } 127 }
118} 128}
119 129
@@ -127,12 +137,11 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
127 * 1: ResultCode 137 * 1: ResultCode
128 */ 138 */
129void SRV::Subscribe(Kernel::HLERequestContext& ctx) { 139void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
130 u32* cmd_buff = ctx.CommandBuffer(); 140 IPC::RequestParser rp(ctx, 0x9, 1, 0);
131 141 u32 notification_id = rp.Pop<u32>();
132 u32 notification_id = cmd_buff[1];
133 142
134 cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040 143 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
135 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 144 rb.Push(RESULT_SUCCESS);
136 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); 145 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
137} 146}
138 147
@@ -146,12 +155,11 @@ void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
146 * 1: ResultCode 155 * 1: ResultCode
147 */ 156 */
148void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { 157void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
149 u32* cmd_buff = ctx.CommandBuffer(); 158 IPC::RequestParser rp(ctx, 0xA, 1, 0);
159 u32 notification_id = rp.Pop<u32>();
150 160
151 u32 notification_id = cmd_buff[1]; 161 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
152 162 rb.Push(RESULT_SUCCESS);
153 cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040
154 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
155 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); 163 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
156} 164}
157 165
@@ -166,13 +174,12 @@ void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
166 * 1: ResultCode 174 * 1: ResultCode
167 */ 175 */
168void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { 176void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
169 u32* cmd_buff = ctx.CommandBuffer(); 177 IPC::RequestParser rp(ctx, 0xC, 2, 0);
170 178 u32 notification_id = rp.Pop<u32>();
171 u32 notification_id = cmd_buff[1]; 179 u8 flags = rp.Pop<u8>();
172 u8 flags = cmd_buff[2] & 0xFF;
173 180
174 cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040 181 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
175 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 182 rb.Push(RESULT_SUCCESS);
176 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, 183 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id,
177 flags); 184 flags);
178} 185}
diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h
index 3f5355fa9..0b09f2299 100644
--- a/src/video_core/regs_texturing.h
+++ b/src/video_core/regs_texturing.h
@@ -30,10 +30,10 @@ struct TexturingRegs {
30 Repeat = 2, 30 Repeat = 2,
31 MirroredRepeat = 3, 31 MirroredRepeat = 3,
32 // Mode 4-7 produces some weird result and may be just invalid: 32 // Mode 4-7 produces some weird result and may be just invalid:
33 // 4: Positive coord: clamp to edge; negative coord: repeat 33 ClampToEdge2 = 4, // Positive coord: clamp to edge; negative coord: repeat
34 // 5: Positive coord: clamp to border; negative coord: repeat 34 ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat
35 // 6: Repeat 35 Repeat2 = 6, // Same as Repeat
36 // 7: Repeat 36 Repeat3 = 7, // Same as Repeat
37 }; 37 };
38 38
39 enum TextureFilter : u32 { 39 enum TextureFilter : u32 {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 57d5e8253..e6cccebf6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -182,19 +182,22 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
182RasterizerOpenGL::~RasterizerOpenGL() {} 182RasterizerOpenGL::~RasterizerOpenGL() {}
183 183
184/** 184/**
185 * This is a helper function to resolve an issue with opposite quaternions being interpolated by 185 * This is a helper function to resolve an issue when interpolating opposite quaternions. See below
186 * OpenGL. See below for a detailed description of this issue (yuriks): 186 * for a detailed description of this issue (yuriks):
187 * 187 *
188 * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you 188 * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you
189 * interpolate two quaternions that are opposite, instead of going from one rotation to another 189 * interpolate two quaternions that are opposite, instead of going from one rotation to another
190 * using the shortest path, you'll go around the longest path. You can test if two quaternions are 190 * using the shortest path, you'll go around the longest path. You can test if two quaternions are
191 * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore 191 * opposite by checking if Dot(Q1, Q2) < 0. In that case, you can flip either of them, therefore
192 * making Dot(-Q1, W2) positive. 192 * making Dot(Q1, -Q2) positive.
193 * 193 *
194 * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This 194 * This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This is
195 * should be correct for nearly all cases, however a more correct implementation (but less trivial 195 * correct for most cases but can still rotate around the long way sometimes. An implementation
196 * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions 196 * which did `lerp(lerp(Q1, Q2), Q3)` (with proper weighting), applying the dot product check
197 * manually using two Lerps, and doing this correction before each Lerp. 197 * between each step would work for those cases at the cost of being more complex to implement.
198 *
199 * Fortunately however, the 3DS hardware happens to also use this exact same logic to work around
200 * these issues, making this basic implementation actually more accurate to the hardware.
198 */ 201 */
199static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) { 202static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) {
200 Math::Vec4f a{qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32()}; 203 Math::Vec4f a{qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32()};
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 93d7b0b71..70298e211 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -55,6 +55,12 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
55 GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder 55 GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder
56 GL_REPEAT, // WrapMode::Repeat 56 GL_REPEAT, // WrapMode::Repeat
57 GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat 57 GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat
58 // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the
59 // comments in enum WrapMode.
60 GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2
61 GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2
62 GL_REPEAT, // WrapMode::Repeat2
63 GL_REPEAT, // WrapMode::Repeat3
58 }; 64 };
59 65
60 // Range check table for input 66 // Range check table for input
@@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
65 return GL_CLAMP_TO_EDGE; 71 return GL_CLAMP_TO_EDGE;
66 } 72 }
67 73
74 if (static_cast<u32>(mode) > 3) {
75 // It is still unclear whether mode 4-7 are valid, so log it if a game uses them.
76 // TODO(wwylele): telemetry should be added here so we can collect more info about which
77 // game uses this.
78 LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode));
79 }
80
68 GLenum gl_mode = wrap_mode_table[mode]; 81 GLenum gl_mode = wrap_mode_table[mode];
69 82
70 // Check for dummy values indicating an unknown mode 83 // Check for dummy values indicating an unknown mode
diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp
index 8b7b1defb..cd7b6c39d 100644
--- a/src/video_core/swrasterizer/rasterizer.cpp
+++ b/src/video_core/swrasterizer/rasterizer.cpp
@@ -357,10 +357,22 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
357 int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))) 357 int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height)))
358 .ToFloat32(); 358 .ToFloat32();
359 359
360 if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder && 360 bool use_border_s = false;
361 (s < 0 || static_cast<u32>(s) >= texture.config.width)) || 361 bool use_border_t = false;
362 (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder && 362
363 (t < 0 || static_cast<u32>(t) >= texture.config.height))) { 363 if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder) {
364 use_border_s = s < 0 || s >= static_cast<int>(texture.config.width);
365 } else if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder2) {
366 use_border_s = s >= static_cast<int>(texture.config.width);
367 }
368
369 if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder) {
370 use_border_t = t < 0 || t >= static_cast<int>(texture.config.height);
371 } else if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder2) {
372 use_border_t = t >= static_cast<int>(texture.config.height);
373 }
374
375 if (use_border_s || use_border_t) {
364 auto border_color = texture.config.border_color; 376 auto border_color = texture.config.border_color;
365 texture_color[i] = {border_color.r, border_color.g, border_color.b, 377 texture_color[i] = {border_color.r, border_color.g, border_color.b,
366 border_color.a}; 378 border_color.a};
diff --git a/src/video_core/swrasterizer/texturing.cpp b/src/video_core/swrasterizer/texturing.cpp
index aeb6aeb8c..4f02b93f2 100644
--- a/src/video_core/swrasterizer/texturing.cpp
+++ b/src/video_core/swrasterizer/texturing.cpp
@@ -18,22 +18,33 @@ using TevStageConfig = TexturingRegs::TevStageConfig;
18 18
19int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) { 19int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) {
20 switch (mode) { 20 switch (mode) {
21 case TexturingRegs::TextureConfig::ClampToEdge2:
22 // For negative coordinate, ClampToEdge2 behaves the same as Repeat
23 if (val < 0) {
24 return static_cast<int>(static_cast<unsigned>(val) % size);
25 }
26 // [[fallthrough]]
21 case TexturingRegs::TextureConfig::ClampToEdge: 27 case TexturingRegs::TextureConfig::ClampToEdge:
22 val = std::max(val, 0); 28 val = std::max(val, 0);
23 val = std::min(val, (int)size - 1); 29 val = std::min(val, static_cast<int>(size) - 1);
24 return val; 30 return val;
25 31
26 case TexturingRegs::TextureConfig::ClampToBorder: 32 case TexturingRegs::TextureConfig::ClampToBorder:
27 return val; 33 return val;
28 34
35 case TexturingRegs::TextureConfig::ClampToBorder2:
36 // For ClampToBorder2, the case of positive coordinate beyond the texture size is already
37 // handled outside. Here we only handle the negative coordinate in the same way as Repeat.
38 case TexturingRegs::TextureConfig::Repeat2:
39 case TexturingRegs::TextureConfig::Repeat3:
29 case TexturingRegs::TextureConfig::Repeat: 40 case TexturingRegs::TextureConfig::Repeat:
30 return (int)((unsigned)val % size); 41 return static_cast<int>(static_cast<unsigned>(val) % size);
31 42
32 case TexturingRegs::TextureConfig::MirroredRepeat: { 43 case TexturingRegs::TextureConfig::MirroredRepeat: {
33 unsigned int coord = ((unsigned)val % (2 * size)); 44 unsigned int coord = (static_cast<unsigned>(val) % (2 * size));
34 if (coord >= size) 45 if (coord >= size)
35 coord = 2 * size - 1 - coord; 46 coord = 2 * size - 1 - coord;
36 return (int)coord; 47 return static_cast<int>(coord);
37 } 48 }
38 49
39 default: 50 default: