summaryrefslogtreecommitdiffstats
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) {
namespace IPC {
+/// Size of the command buffer area, in 32-bit words.
+constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
+
// These errors are commonly returned by invalid IPC translations, so alias them here for
// convenience.
// 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 @@
#pragma once
+#include <array>
+#include <tuple>
+#include <type_traits>
+#include <utility>
#include "core/hle/ipc.h"
#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
namespace IPC {
class RequestHelperBase {
protected:
+ Kernel::HLERequestContext* context = nullptr;
u32* cmdbuf;
ptrdiff_t index = 1;
Header header;
public:
+ RequestHelperBase(Kernel::HLERequestContext& context, Header desired_header)
+ : context(&context), cmdbuf(context.CommandBuffer()), header(desired_header) {}
+
RequestHelperBase(u32* command_buffer, Header command_header)
: cmdbuf(command_buffer), header(command_header) {}
@@ -51,12 +60,27 @@ public:
class RequestBuilder : public RequestHelperBase {
public:
+ RequestBuilder(Kernel::HLERequestContext& context, Header command_header)
+ : RequestHelperBase(context, command_header) {
+ // From this point we will start overwriting the existing command buffer, so it's safe to
+ // release all previous incoming Object pointers since they won't be usable anymore.
+ context.ClearIncomingObjects();
+ cmdbuf[0] = header.raw;
+ }
+
+ RequestBuilder(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size,
+ unsigned translate_params_size)
+ : RequestBuilder(
+ context, Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {}
+
RequestBuilder(u32* command_buffer, Header command_header)
: RequestHelperBase(command_buffer, command_header) {
cmdbuf[0] = header.raw;
}
+
explicit RequestBuilder(u32* command_buffer, u32 command_header)
: RequestBuilder(command_buffer, Header{command_header}) {}
+
RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size,
unsigned translate_params_size)
: RequestBuilder(command_buffer,
@@ -88,6 +112,9 @@ public:
template <typename... H>
void PushMoveHandles(H... handles);
+ template <typename... O>
+ void PushObjects(Kernel::SharedPtr<O>... pointers);
+
void PushCurrentPIDHandle();
void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id);
@@ -153,6 +180,11 @@ inline void RequestBuilder::PushMoveHandles(H... handles) {
Push(static_cast<Kernel::Handle>(handles)...);
}
+template <typename... O>
+inline void RequestBuilder::PushObjects(Kernel::SharedPtr<O>... pointers) {
+ PushMoveHandles(context->AddOutgoingHandle(std::move(pointers))...);
+}
+
inline void RequestBuilder::PushCurrentPIDHandle() {
Push(CallingPidDesc());
Push(u32(0));
@@ -171,10 +203,21 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size,
class RequestParser : public RequestHelperBase {
public:
+ RequestParser(Kernel::HLERequestContext& context, Header desired_header)
+ : RequestHelperBase(context, desired_header) {}
+
+ RequestParser(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size,
+ unsigned translate_params_size)
+ : RequestParser(context,
+ Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {
+ }
+
RequestParser(u32* command_buffer, Header command_header)
: RequestHelperBase(command_buffer, command_header) {}
+
explicit RequestParser(u32* command_buffer, u32 command_header)
: RequestParser(command_buffer, Header{command_header}) {}
+
RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size,
unsigned translate_params_size)
: RequestParser(command_buffer,
@@ -186,7 +229,10 @@ public:
ValidateHeader();
Header builderHeader{
MakeHeader(header.command_id, normal_params_size, translate_params_size)};
- return {cmdbuf, builderHeader};
+ if (context != nullptr)
+ return {*context, builderHeader};
+ else
+ return {cmdbuf, builderHeader};
}
template <typename T>
@@ -198,10 +244,52 @@ public:
template <typename First, typename... Other>
void Pop(First& first_value, Other&... other_values);
+ /// Equivalent to calling `PopHandles<1>()[0]`.
Kernel::Handle PopHandle();
+ /**
+ * Pops a descriptor containing `N` handles. The handles are returned as an array. The
+ * descriptor must contain exactly `N` handles, it is not permitted to, for example, call
+ * PopHandles<1>() twice to read a multi-handle descriptor with 2 handles, or to make a single
+ * PopHandles<2>() call to read 2 single-handle descriptors.
+ */
+ template <unsigned int N>
+ std::array<Kernel::Handle, N> PopHandles();
+
+ /// Convenience wrapper around PopHandles() which assigns the handles to the passed references.
template <typename... H>
- void PopHandles(H&... handles);
+ void PopHandles(H&... handles) {
+ std::tie(handles...) = PopHandles<sizeof...(H)>();
+ }
+
+ /// Equivalent to calling `PopGenericObjects<1>()[0]`.
+ Kernel::SharedPtr<Kernel::Object> PopGenericObject();
+
+ /// Equivalent to calling `std::get<0>(PopObjects<T>())`.
+ template <typename T>
+ Kernel::SharedPtr<T> PopObject();
+
+ /**
+ * Pop a descriptor containing `N` handles and resolves them to Kernel::Object pointers. If a
+ * handle is invalid, null is returned for that object instead. The same caveats from
+ * PopHandles() apply regarding `N` matching the number of handles in the descriptor.
+ */
+ template <unsigned int N>
+ std::array<Kernel::SharedPtr<Kernel::Object>, N> PopGenericObjects();
+
+ /**
+ * Resolves handles to Kernel::Objects as in PopGenericsObjects(), but then also casts them to
+ * the passed `T` types, while verifying that the cast is valid. If the type of an object does
+ * not match, null is returned instead.
+ */
+ template <typename... T>
+ std::tuple<Kernel::SharedPtr<T>...> PopObjects();
+
+ /// Convenience wrapper around PopObjects() which assigns the handles to the passed references.
+ template <typename... T>
+ void PopObjects(Kernel::SharedPtr<T>&... pointers) {
+ std::tie(pointers...) = PopObjects<T...>();
+ }
/**
* @brief Pops the static buffer vaddr
@@ -313,15 +401,54 @@ inline Kernel::Handle RequestParser::PopHandle() {
return Pop<Kernel::Handle>();
}
-template <typename... H>
-void RequestParser::PopHandles(H&... handles) {
- const u32 handle_descriptor = Pop<u32>();
- const int handles_number = sizeof...(H);
- DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
- "Tried to pop handle(s) but the descriptor is not a handle descriptor");
- DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor),
- "Number of handles doesn't match the descriptor");
- Pop(static_cast<Kernel::Handle&>(handles)...);
+template <unsigned int N>
+std::array<Kernel::Handle, N> RequestParser::PopHandles() {
+ u32 handle_descriptor = Pop<u32>();
+ ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
+ "Tried to pop handle(s) but the descriptor is not a handle descriptor");
+ ASSERT_MSG(N == HandleNumberFromDesc(handle_descriptor),
+ "Number of handles doesn't match the descriptor");
+
+ std::array<Kernel::Handle, N> handles{};
+ for (Kernel::Handle& handle : handles) {
+ handle = Pop<Kernel::Handle>();
+ }
+ return handles;
+}
+
+inline Kernel::SharedPtr<Kernel::Object> RequestParser::PopGenericObject() {
+ Kernel::Handle handle = PopHandle();
+ return context->GetIncomingHandle(handle);
+}
+
+template <typename T>
+Kernel::SharedPtr<T> RequestParser::PopObject() {
+ return Kernel::DynamicObjectCast<T>(PopGenericObject());
+}
+
+template <unsigned int N>
+inline std::array<Kernel::SharedPtr<Kernel::Object>, N> RequestParser::PopGenericObjects() {
+ std::array<Kernel::Handle, N> handles = PopHandles<N>();
+ std::array<Kernel::SharedPtr<Kernel::Object>, N> pointers;
+ for (int i = 0; i < N; ++i) {
+ pointers[i] = context->GetIncomingHandle(handles[i]);
+ }
+ return pointers;
+}
+
+namespace detail {
+template <typename... T, size_t... I>
+std::tuple<Kernel::SharedPtr<T>...> PopObjectsHelper(
+ std::array<Kernel::SharedPtr<Kernel::Object>, sizeof...(T)>&& pointers,
+ std::index_sequence<I...>) {
+ return std::make_tuple(Kernel::DynamicObjectCast<T>(std::move(pointers[I]))...);
+}
+} // namespace detail
+
+template <typename... T>
+inline std::tuple<Kernel::SharedPtr<T>...> RequestParser::PopObjects() {
+ return detail::PopObjectsHelper<T...>(PopGenericObjects<sizeof...(T)>(),
+ std::index_sequence_for<T...>{});
}
inline 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 @@
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_types.h"
+#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
namespace Kernel {
@@ -23,4 +25,101 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
HLERequestContext::~HLERequestContext() = default;
+SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const {
+ ASSERT(id_from_cmdbuf < request_handles.size());
+ return request_handles[id_from_cmdbuf];
+}
+
+u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) {
+ request_handles.push_back(std::move(object));
+ return request_handles.size() - 1;
+}
+
+void HLERequestContext::ClearIncomingObjects() {
+ request_handles.clear();
+}
+
+ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
+ Process& src_process,
+ HandleTable& src_table) {
+ IPC::Header header{src_cmdbuf[0]};
+
+ size_t untranslated_size = 1u + header.normal_params_size;
+ size_t command_size = untranslated_size + header.translate_params_size;
+ ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error
+
+ std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin());
+
+ size_t i = untranslated_size;
+ while (i < command_size) {
+ u32 descriptor = cmd_buf[i] = src_cmdbuf[i];
+ i += 1;
+
+ switch (IPC::GetDescriptorType(descriptor)) {
+ case IPC::DescriptorType::CopyHandle:
+ case IPC::DescriptorType::MoveHandle: {
+ u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
+ ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error
+ for (u32 j = 0; j < num_handles; ++j) {
+ Handle handle = src_cmdbuf[i];
+ SharedPtr<Object> object = src_table.GetGeneric(handle);
+ ASSERT(object != nullptr); // TODO(yuriks): Return error
+ if (descriptor == IPC::DescriptorType::MoveHandle) {
+ src_table.Close(handle);
+ }
+
+ cmd_buf[i++] = AddOutgoingHandle(std::move(object));
+ }
+ break;
+ }
+ case IPC::DescriptorType::CallingPid: {
+ cmd_buf[i++] = src_process.process_id;
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
+ HandleTable& dst_table) const {
+ IPC::Header header{cmd_buf[0]};
+
+ size_t untranslated_size = 1u + header.normal_params_size;
+ size_t command_size = untranslated_size + header.translate_params_size;
+ ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH);
+
+ std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf);
+
+ size_t i = untranslated_size;
+ while (i < command_size) {
+ u32 descriptor = dst_cmdbuf[i] = cmd_buf[i];
+ i += 1;
+
+ switch (IPC::GetDescriptorType(descriptor)) {
+ case IPC::DescriptorType::CopyHandle:
+ case IPC::DescriptorType::MoveHandle: {
+ // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally
+ u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
+ ASSERT(i + num_handles <= command_size);
+ for (u32 j = 0; j < num_handles; ++j) {
+ SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]);
+
+ // TODO(yuriks): Figure out the proper error handling for if this fails
+ Handle handle = dst_table.Create(object).Unwrap();
+ dst_cmdbuf[i++] = handle;
+ }
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
} // 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 @@
#pragma once
+#include <array>
#include <memory>
#include <vector>
+#include <boost/container/small_vector.hpp>
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/server_session.h"
@@ -15,6 +20,9 @@ class ServiceFrameworkBase;
namespace Kernel {
+class HandleTable;
+class Process;
+
/**
* Interface implemented by HLE Session handlers.
* This can be provided to a ServerSession in order to hook into several relevant events
@@ -59,14 +67,28 @@ protected:
* Class containing information about an in-flight IPC request being handled by an HLE service
* implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and
* when possible use the APIs in this class to service the request.
+ *
+ * HLE handle protocol
+ * ===================
+ *
+ * To avoid needing HLE services to keep a separate handle table, or having to directly modify the
+ * requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel
+ * will decode the incoming handles into object pointers and insert a id in the buffer where the
+ * handle would normally be. The service then calls GetIncomingHandle() with that id to get the
+ * pointer to the object. Similarly, instead of inserting a handle into the command buffer, the
+ * service calls AddOutgoingHandle() and stores the returned id where the handle would normally go.
+ *
+ * The end result is similar to just giving services their own real handle tables, but since these
+ * ids are local to a specific context, it avoids requiring services to manage handles for objects
+ * across multiple calls and ensuring that unneeded handles are cleaned up.
*/
class HLERequestContext {
public:
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
- u32* CommandBuffer() const {
- return cmd_buf;
+ u32* CommandBuffer() {
+ return cmd_buf.data();
}
/**
@@ -77,11 +99,37 @@ public:
return session;
}
+ /**
+ * Resolves a object id from the request command buffer into a pointer to an object. See the
+ * "HLE handle protocol" section in the class documentation for more details.
+ */
+ SharedPtr<Object> GetIncomingHandle(u32 id_from_cmdbuf) const;
+
+ /**
+ * Adds an outgoing object to the response, returning the id which should be used to reference
+ * it. See the "HLE handle protocol" section in the class documentation for more details.
+ */
+ u32 AddOutgoingHandle(SharedPtr<Object> object);
+
+ /**
+ * Discards all Objects from the context, invalidating all ids. This may be called after reading
+ * out all incoming objects, so that the buffer memory can be re-used for outgoing handles, but
+ * this is not required.
+ */
+ void ClearIncomingObjects();
+
private:
friend class Service::ServiceFrameworkBase;
- u32* cmd_buf = nullptr;
+ ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process,
+ HandleTable& src_table);
+ ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
+ HandleTable& dst_table) const;
+
+ std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<ServerSession> session;
+ // TODO(yuriks): Check common usage of this and optimize size accordingly
+ boost::container::small_vector<SharedPtr<Object>, 8> request_handles;
};
} // 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) {
rb.Push(RESULT_SUCCESS);
rb.PushRaw(connection_status);
+ // Reset the bitmask of changed nodes after each call to this
+ // function to prevent falsely informing games of outstanding
+ // changes in subsequent calls.
+ connection_status.changed_nodes = 0;
+
LOG_DEBUG(Service_NWM, "called");
}
@@ -314,8 +319,11 @@ static void BeginHostingNetwork(Interface* self) {
// The host is always the first node
connection_status.network_node_id = 1;
node_info[0].network_node_id = 1;
+ connection_status.nodes[0] = connection_status.network_node_id;
// Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
connection_status.node_bitmask |= 1;
+ // Notify the application that the first node was set.
+ connection_status.changed_nodes |= 1;
// If the game has a preferred channel, use that instead.
if (network_info.channel != 0)
@@ -352,6 +360,8 @@ static void DestroyNetwork(Interface* self) {
// Unschedule the beacon broadcast event.
CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
+ // TODO(Subv): Check if connection_status is indeed reset after this call.
+ connection_status = {};
connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
connection_status_event->Signal();
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;
// Interval measured in TU, the default value is 100TU = 102.4ms
const u16 DefaultBeaconInterval = 100;
+/// The maximum number of nodes that can exist in an UDS session.
+constexpr u32 UDSMaxNodes = 16;
+
struct NodeInfo {
u64_le friend_code_seed;
std::array<u16_le, 10> username;
@@ -47,8 +50,8 @@ struct ConnectionStatus {
u32_le status;
INSERT_PADDING_WORDS(1);
u16_le network_node_id;
- INSERT_PADDING_BYTES(2);
- INSERT_PADDING_BYTES(32);
+ u16_le changed_nodes;
+ u16_le nodes[UDSMaxNodes];
u8 total_nodes;
u8 max_nodes;
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 {
namespace NWM {
using MacAddress = std::array<u8, 6>;
-
-/// The maximum number of nodes that can exist in an UDS session.
-constexpr u32 UDSMaxNodes = 16;
constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
/// 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <fmt/format.h>
+#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
+#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/service/ac/ac.h"
@@ -160,12 +164,6 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
u32* cmd_buf = Kernel::GetCommandBuffer();
- // TODO(yuriks): The kernel should be the one handling this as part of translation after
- // everything else is migrated
- Kernel::HLERequestContext context;
- context.cmd_buf = cmd_buf;
- context.session = std::move(server_session);
-
u32 header_code = cmd_buf[0];
auto itr = handlers.find(header_code);
const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
@@ -173,9 +171,18 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses
return ReportUnimplementedFunction(cmd_buf, info);
}
+ // TODO(yuriks): The kernel should be the one handling this as part of translation after
+ // everything else is migrated
+ Kernel::HLERequestContext context;
+ context.session = std::move(server_session);
+ context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
+ Kernel::g_handle_table);
+
LOG_TRACE(Service, "%s",
MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str());
handler_invoker(this, info->handler_callback, context);
+ context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
+ Kernel::g_handle_table);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
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 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/ipc.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/service/sm/sm.h"
@@ -30,15 +32,18 @@ constexpr int MAX_PENDING_NOTIFICATIONS = 16;
* 1: ResultCode
*/
void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
- u32* cmd_buff = ctx.CommandBuffer();
+ IPC::RequestParser rp(ctx, 0x1, 0, 2);
- if (cmd_buff[1] != IPC::CallingPidDesc()) {
- cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40
- cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
+ u32 pid_descriptor = rp.Pop<u32>();
+ if (pid_descriptor != IPC::CallingPidDesc()) {
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(IPC::ERR_INVALID_BUFFER_DESCRIPTOR);
return;
}
- cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ u32 caller_pid = rp.Pop<u32>();
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_SRV, "(STUBBED) called");
}
@@ -53,15 +58,14 @@ void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
* 3: Handle to semaphore signaled on process notification
*/
void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
- u32* cmd_buff = ctx.CommandBuffer();
+ IPC::RequestParser rp(ctx, 0x2, 0, 0);
notification_semaphore =
Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap();
- cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = IPC::CopyHandleDesc(1);
- cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom();
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
+ rb.Push(RESULT_SUCCESS);
+ rb.PushObjects(notification_semaphore);
LOG_WARNING(Service_SRV, "(STUBBED) called");
}
@@ -77,43 +81,49 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
* 3: Service handle
*/
void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
- ResultCode res = RESULT_SUCCESS;
- u32* cmd_buff = ctx.CommandBuffer();
+ IPC::RequestParser rp(ctx, 0x5, 4, 0);
+ auto name_buf = rp.PopRaw<std::array<char, 8>>();
+ size_t name_len = rp.Pop<u32>();
+ u32 flags = rp.Pop<u32>();
+
+ bool return_port_on_failure = (flags & 1) == 0;
- size_t name_len = cmd_buff[3];
if (name_len > Service::kMaxPortSize) {
- cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw;
- LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len,
- cmd_buff[1]);
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(ERR_INVALID_NAME_SIZE);
+ LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len);
return;
}
- std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len);
- bool return_port_on_failure = (cmd_buff[4] & 1) == 0;
+ std::string name(name_buf.data(), name_len);
// TODO(yuriks): Permission checks go here
auto client_port = service_manager->GetServicePort(name);
if (client_port.Failed()) {
- cmd_buff[1] = client_port.Code().raw;
- LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(),
- cmd_buff[1]);
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(client_port.Code());
+ LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(),
+ client_port.Code().raw);
return;
}
auto session = client_port.Unwrap()->Connect();
- cmd_buff[1] = session.Code().raw;
if (session.Succeeded()) {
- cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom();
- LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(),
- cmd_buff[3]);
+ LOG_DEBUG(Service_SRV, "called service=%s -> session=%u", name.c_str(),
+ (*session)->GetObjectId());
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
+ rb.Push(session.Code());
+ rb.PushObjects(session.MoveFrom());
} else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) {
- cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw;
- cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom();
- LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(),
- cmd_buff[3]);
+ LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u",
+ name.c_str(), (*client_port)->GetObjectId());
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
+ rb.Push(ERR_MAX_CONNECTIONS_REACHED);
+ rb.PushObjects(client_port.MoveFrom());
} else {
- LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(),
- cmd_buff[1]);
+ LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code());
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(session.Code());
}
}
@@ -127,12 +137,11 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
* 1: ResultCode
*/
void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
- u32* cmd_buff = ctx.CommandBuffer();
-
- u32 notification_id = cmd_buff[1];
+ IPC::RequestParser rp(ctx, 0x9, 1, 0);
+ u32 notification_id = rp.Pop<u32>();
- cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
}
@@ -146,12 +155,11 @@ void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
* 1: ResultCode
*/
void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
- u32* cmd_buff = ctx.CommandBuffer();
+ IPC::RequestParser rp(ctx, 0xA, 1, 0);
+ u32 notification_id = rp.Pop<u32>();
- u32 notification_id = cmd_buff[1];
-
- cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
}
@@ -166,13 +174,12 @@ void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
* 1: ResultCode
*/
void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
- u32* cmd_buff = ctx.CommandBuffer();
-
- u32 notification_id = cmd_buff[1];
- u8 flags = cmd_buff[2] & 0xFF;
+ IPC::RequestParser rp(ctx, 0xC, 2, 0);
+ u32 notification_id = rp.Pop<u32>();
+ u8 flags = rp.Pop<u8>();
- cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id,
flags);
}
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 {
Repeat = 2,
MirroredRepeat = 3,
// Mode 4-7 produces some weird result and may be just invalid:
- // 4: Positive coord: clamp to edge; negative coord: repeat
- // 5: Positive coord: clamp to border; negative coord: repeat
- // 6: Repeat
- // 7: Repeat
+ ClampToEdge2 = 4, // Positive coord: clamp to edge; negative coord: repeat
+ ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat
+ Repeat2 = 6, // Same as Repeat
+ Repeat3 = 7, // Same as Repeat
};
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) {
RasterizerOpenGL::~RasterizerOpenGL() {}
/**
- * This is a helper function to resolve an issue with opposite quaternions being interpolated by
- * OpenGL. See below for a detailed description of this issue (yuriks):
+ * This is a helper function to resolve an issue when interpolating opposite quaternions. See below
+ * for a detailed description of this issue (yuriks):
*
* For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you
* interpolate two quaternions that are opposite, instead of going from one rotation to another
* using the shortest path, you'll go around the longest path. You can test if two quaternions are
- * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore
- * making Dot(-Q1, W2) positive.
+ * opposite by checking if Dot(Q1, Q2) < 0. In that case, you can flip either of them, therefore
+ * making Dot(Q1, -Q2) positive.
*
- * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This
- * should be correct for nearly all cases, however a more correct implementation (but less trivial
- * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions
- * manually using two Lerps, and doing this correction before each Lerp.
+ * This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This is
+ * correct for most cases but can still rotate around the long way sometimes. An implementation
+ * which did `lerp(lerp(Q1, Q2), Q3)` (with proper weighting), applying the dot product check
+ * between each step would work for those cases at the cost of being more complex to implement.
+ *
+ * Fortunately however, the 3DS hardware happens to also use this exact same logic to work around
+ * these issues, making this basic implementation actually more accurate to the hardware.
*/
static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) {
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) {
GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder
GL_REPEAT, // WrapMode::Repeat
GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat
+ // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the
+ // comments in enum WrapMode.
+ GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2
+ GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2
+ GL_REPEAT, // WrapMode::Repeat2
+ GL_REPEAT, // WrapMode::Repeat3
};
// Range check table for input
@@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
return GL_CLAMP_TO_EDGE;
}
+ if (static_cast<u32>(mode) > 3) {
+ // It is still unclear whether mode 4-7 are valid, so log it if a game uses them.
+ // TODO(wwylele): telemetry should be added here so we can collect more info about which
+ // game uses this.
+ LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode));
+ }
+
GLenum gl_mode = wrap_mode_table[mode];
// 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
int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height)))
.ToFloat32();
- if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder &&
- (s < 0 || static_cast<u32>(s) >= texture.config.width)) ||
- (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder &&
- (t < 0 || static_cast<u32>(t) >= texture.config.height))) {
+ bool use_border_s = false;
+ bool use_border_t = false;
+
+ if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder) {
+ use_border_s = s < 0 || s >= static_cast<int>(texture.config.width);
+ } else if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder2) {
+ use_border_s = s >= static_cast<int>(texture.config.width);
+ }
+
+ if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder) {
+ use_border_t = t < 0 || t >= static_cast<int>(texture.config.height);
+ } else if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder2) {
+ use_border_t = t >= static_cast<int>(texture.config.height);
+ }
+
+ if (use_border_s || use_border_t) {
auto border_color = texture.config.border_color;
texture_color[i] = {border_color.r, border_color.g, border_color.b,
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;
int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) {
switch (mode) {
+ case TexturingRegs::TextureConfig::ClampToEdge2:
+ // For negative coordinate, ClampToEdge2 behaves the same as Repeat
+ if (val < 0) {
+ return static_cast<int>(static_cast<unsigned>(val) % size);
+ }
+ // [[fallthrough]]
case TexturingRegs::TextureConfig::ClampToEdge:
val = std::max(val, 0);
- val = std::min(val, (int)size - 1);
+ val = std::min(val, static_cast<int>(size) - 1);
return val;
case TexturingRegs::TextureConfig::ClampToBorder:
return val;
+ case TexturingRegs::TextureConfig::ClampToBorder2:
+ // For ClampToBorder2, the case of positive coordinate beyond the texture size is already
+ // handled outside. Here we only handle the negative coordinate in the same way as Repeat.
+ case TexturingRegs::TextureConfig::Repeat2:
+ case TexturingRegs::TextureConfig::Repeat3:
case TexturingRegs::TextureConfig::Repeat:
- return (int)((unsigned)val % size);
+ return static_cast<int>(static_cast<unsigned>(val) % size);
case TexturingRegs::TextureConfig::MirroredRepeat: {
- unsigned int coord = ((unsigned)val % (2 * size));
+ unsigned int coord = (static_cast<unsigned>(val) % (2 * size));
if (coord >= size)
coord = 2 * size - 1 - coord;
- return (int)coord;
+ return static_cast<int>(coord);
}
default: