diff options
| m--------- | externals/boost | 0 | ||||
| -rw-r--r-- | src/core/hle/ipc.h | 3 | ||||
| -rw-r--r-- | src/core/hle/ipc_helpers.h | 149 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 99 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 54 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 19 | ||||
| -rw-r--r-- | src/core/hle/service/sm/srv.cpp | 105 | ||||
| -rw-r--r-- | src/video_core/regs_lighting.h | 63 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 45 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 6 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 39 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.h | 3 | 
12 files changed, 488 insertions, 97 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/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_lighting.h b/src/video_core/regs_lighting.h index 6793405d9..fbfebc0a7 100644 --- a/src/video_core/regs_lighting.h +++ b/src/video_core/regs_lighting.h @@ -26,6 +26,16 @@ struct LightingRegs {          DistanceAttenuation = 16,      }; +    static LightingSampler SpotlightAttenuationSampler(unsigned index) { +        return static_cast<LightingSampler>( +            static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); +    } + +    static LightingSampler DistanceAttenuationSampler(unsigned index) { +        return static_cast<LightingSampler>( +            static_cast<unsigned>(LightingSampler::DistanceAttenuation) + index); +    } +      /**      * Pica fragment lighting supports using different LUTs for each lighting component:  Reflectance      * R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor, @@ -73,6 +83,8 @@ struct LightingRegs {          VH = 1, // Cosine of the angle between the view and half-angle vectors          NV = 2, // Cosine of the angle between the normal and the view vector          LN = 3, // Cosine of the angle between the light and the normal vectors +        SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors +        CP = 5, // TODO: document and implement      };      enum class LightingBumpMode : u32 { @@ -104,6 +116,9 @@ struct LightingRegs {              return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&                     (config != LightingConfig::Config5); +        case LightingSampler::SpotlightAttenuation: +            return (config != LightingConfig::Config2) && (config != LightingConfig::Config3); +          case LightingSampler::Fresnel:              return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&                     (config != LightingConfig::Config4); @@ -116,11 +131,10 @@ struct LightingRegs {              return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||                     (config == LightingConfig::Config7);          default: -            UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached " -                            "unreachable section, sampler should be one " -                            "of Distribution0, Distribution1, Fresnel, " -                            "ReflectRed, ReflectGreen or ReflectBlue, instead " -                            "got %i", +            UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached unreachable section, " +                            "sampler should be one of Distribution0, Distribution1, " +                            "SpotlightAttenuation, Fresnel, ReflectRed, ReflectGreen or " +                            "ReflectBlue, instead got %i",                              static_cast<int>(config));          }      } @@ -140,7 +154,16 @@ struct LightingRegs {              BitField<0, 16, u32> z;          }; -        INSERT_PADDING_WORDS(0x3); +        // inverse spotlight direction vector, encoded as fixed1.1.11 +        union { +            BitField<0, 13, s32> spot_x; +            BitField<16, 13, s32> spot_y; +        }; +        union { +            BitField<0, 13, s32> spot_z; +        }; + +        INSERT_PADDING_WORDS(0x1);          union {              BitField<0, 1, u32> directional; @@ -169,8 +192,16 @@ struct LightingRegs {      } config0;      union { +        u32 raw; + +        // Each bit specifies whether spot light attenuation should be applied for the corresponding +        // light. +        BitField<8, 8, u32> disable_spot_atten; +          BitField<16, 1, u32> disable_lut_d0;          BitField<17, 1, u32> disable_lut_d1; +        // Note: by intuition, BitField<18, 1, u32> should be disable_lut_sp, but it is actually a +        // dummy bit which is always set as 1.          BitField<19, 1, u32> disable_lut_fr;          BitField<20, 1, u32> disable_lut_rr;          BitField<21, 1, u32> disable_lut_rg; @@ -178,23 +209,15 @@ struct LightingRegs {          // Each bit specifies whether distance attenuation should be applied for the corresponding          // light. -        BitField<24, 1, u32> disable_dist_atten_light_0; -        BitField<25, 1, u32> disable_dist_atten_light_1; -        BitField<26, 1, u32> disable_dist_atten_light_2; -        BitField<27, 1, u32> disable_dist_atten_light_3; -        BitField<28, 1, u32> disable_dist_atten_light_4; -        BitField<29, 1, u32> disable_dist_atten_light_5; -        BitField<30, 1, u32> disable_dist_atten_light_6; -        BitField<31, 1, u32> disable_dist_atten_light_7; +        BitField<24, 8, u32> disable_dist_atten;      } config1;      bool IsDistAttenDisabled(unsigned index) const { -        const unsigned disable[] = { -            config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1, -            config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3, -            config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5, -            config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7}; -        return disable[index] != 0; +        return (config1.disable_dist_atten & (1 << index)) != 0; +    } + +    bool IsSpotAttenDisabled(unsigned index) const { +        return (config1.disable_spot_atten & (1 << index)) != 0;      }      union { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 91fff2a63..e6cccebf6 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -738,6 +738,40 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {          SyncLightPosition(7);          break; +    // Fragment spot lighting direction +    case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_x, 0x146 + 0 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_z, 0x147 + 0 * 0x10): +        SyncLightSpotDirection(0); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_x, 0x146 + 1 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_z, 0x147 + 1 * 0x10): +        SyncLightSpotDirection(1); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_x, 0x146 + 2 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_z, 0x147 + 2 * 0x10): +        SyncLightSpotDirection(2); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_x, 0x146 + 3 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_z, 0x147 + 3 * 0x10): +        SyncLightSpotDirection(3); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_x, 0x146 + 4 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_z, 0x147 + 4 * 0x10): +        SyncLightSpotDirection(4); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_x, 0x146 + 5 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_z, 0x147 + 5 * 0x10): +        SyncLightSpotDirection(5); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_x, 0x146 + 6 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_z, 0x147 + 6 * 0x10): +        SyncLightSpotDirection(6); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_x, 0x146 + 7 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_z, 0x147 + 7 * 0x10): +        SyncLightSpotDirection(7); +        break; +      // Fragment lighting light source config      case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10):      case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10): @@ -1598,6 +1632,17 @@ void RasterizerOpenGL::SyncLightPosition(int light_index) {      }  } +void RasterizerOpenGL::SyncLightSpotDirection(int light_index) { +    const auto& light = Pica::g_state.regs.lighting.light[light_index]; +    GLvec3 spot_direction = {light.spot_x / 2047.0f, light.spot_y / 2047.0f, +                             light.spot_z / 2047.0f}; + +    if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) { +        uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; +        uniform_block_data.dirty = true; +    } +} +  void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) {      GLfloat dist_atten_bias =          Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index a9ad7d660..d9a3e9d1c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -125,6 +125,7 @@ private:          alignas(16) GLvec3 diffuse;          alignas(16) GLvec3 ambient;          alignas(16) GLvec3 position; +        alignas(16) GLvec3 spot_direction; // negated          GLfloat dist_atten_bias;          GLfloat dist_atten_scale;      }; @@ -153,7 +154,7 @@ private:      };      static_assert( -        sizeof(UniformData) == 0x3E0, +        sizeof(UniformData) == 0x460,          "The size of the UniformData structure has changed, update the structure in the shader");      static_assert(sizeof(UniformData) < 16384,                    "UniformData structure must be less than 16kb as per the OpenGL spec"); @@ -241,6 +242,9 @@ private:      /// Syncs the specified light's position to match the PICA register      void SyncLightPosition(int light_index); +    /// Syncs the specified spot light direcition to match the PICA register +    void SyncLightSpotDirection(int light_index); +      /// Syncs the specified light's distance attenuation bias to match the PICA register      void SyncLightDistanceAttenuationBias(int light_index); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index ffe419863..db53710aa 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -75,6 +75,8 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {          state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0;          state.lighting.light[light_index].dist_atten_enable =              !regs.lighting.IsDistAttenDisabled(num); +        state.lighting.light[light_index].spot_atten_enable = +            !regs.lighting.IsSpotAttenDisabled(num);      }      state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0; @@ -87,6 +89,12 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {      state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();      state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); +    // this is a dummy field due to lack of the corresponding register +    state.lighting.lut_sp.enable = true; +    state.lighting.lut_sp.abs_input = regs.lighting.abs_lut_input.disable_sp == 0; +    state.lighting.lut_sp.type = regs.lighting.lut_input.sp.Value(); +    state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp); +      state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0;      state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;      state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); @@ -509,7 +517,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {      out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"             "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"             "vec3 light_vector = vec3(0.0);\n" -           "vec3 refl_value = vec3(0.0);\n"; +           "vec3 refl_value = vec3(0.0);\n" +           "vec3 spot_dir = vec3(0.0);\n;";      // Compute fragment normals      if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { @@ -560,6 +569,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {              index = std::string("dot(light_vector, normal)");              break; +        case LightingRegs::LightingLutInput::SP: +            index = std::string("dot(light_vector, spot_dir)"); +            break; +          default:              LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input);              UNIMPLEMENTED(); @@ -596,21 +609,34 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          else              out += "light_vector = normalize(" + light_src + ".position + view);\n"; +        out += "spot_dir = " + light_src + ".spot_direction;\n"; +          // Compute dot product of light_vector and normal, adjust if lighting is one-sided or          // two-sided          std::string dot_product = light_config.two_sided_diffuse                                        ? "abs(dot(light_vector, normal))"                                        : "max(dot(light_vector, normal), 0.0)"; +        // If enabled, compute spot light attenuation value +        std::string spot_atten = "1.0"; +        if (light_config.spot_atten_enable && +            LightingRegs::IsLightingSamplerSupported( +                lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { +            std::string index = +                GetLutIndex(light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); +            auto sampler = LightingRegs::SpotlightAttenuationSampler(light_config.num); +            spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + +                         GetLutValue(sampler, index) + ")"; +        } +          // If enabled, compute distance attenuation value          std::string dist_atten = "1.0";          if (light_config.dist_atten_enable) {              std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " +                                  light_src + ".position) + " + light_src + ".dist_atten_bias)";              index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; -            const unsigned lut_num = -                ((unsigned)LightingRegs::LightingSampler::DistanceAttenuation + light_config.num); -            dist_atten = GetLutValue((LightingRegs::LightingSampler)lut_num, index); +            auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); +            dist_atten = GetLutValue(sampler, index);          }          // If enabled, clamp specular component if lighting result is negative @@ -711,11 +737,11 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          // Compute primary fragment color (diffuse lighting) function          out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + -               light_src + ".ambient) * " + dist_atten + ";\n"; +               light_src + ".ambient) * " + dist_atten + " * " + spot_atten + ";\n";          // Compute secondary fragment color (specular lighting) function          out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + -               clamp_highlights + " * " + dist_atten + ";\n"; +               clamp_highlights + " * " + dist_atten + " * " + spot_atten + ";\n";      }      // Sum final lighting result @@ -967,6 +993,7 @@ struct LightSrc {      vec3 diffuse;      vec3 ambient;      vec3 position; +    vec3 spot_direction;      float dist_atten_bias;      float dist_atten_scale;  }; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index ea6d216d1..9c90eadf9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -93,6 +93,7 @@ union PicaShaderConfig {                  bool directional;                  bool two_sided_diffuse;                  bool dist_atten_enable; +                bool spot_atten_enable;              } light[8];              bool enable; @@ -110,7 +111,7 @@ union PicaShaderConfig {                  bool abs_input;                  Pica::LightingRegs::LightingLutInput type;                  float scale; -            } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb; +            } lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb;          } lighting;          struct {  | 
