diff options
Diffstat (limited to 'src')
| -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 |
11 files changed, 488 insertions, 97 deletions
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 | ||
| 45 | namespace IPC { | 45 | namespace IPC { |
| 46 | 46 | ||
| 47 | /// Size of the command buffer area, in 32-bit words. | ||
| 48 | constexpr 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 | ||
| 11 | namespace IPC { | 16 | namespace IPC { |
| 12 | 17 | ||
| 13 | class RequestHelperBase { | 18 | class RequestHelperBase { |
| 14 | protected: | 19 | protected: |
| 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 | ||
| 19 | public: | 25 | public: |
| 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 | ||
| 52 | class RequestBuilder : public RequestHelperBase { | 61 | class RequestBuilder : public RequestHelperBase { |
| 53 | public: | 62 | public: |
| 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 | ||
| 183 | template <typename... O> | ||
| 184 | inline void RequestBuilder::PushObjects(Kernel::SharedPtr<O>... pointers) { | ||
| 185 | PushMoveHandles(context->AddOutgoingHandle(std::move(pointers))...); | ||
| 186 | } | ||
| 187 | |||
| 156 | inline void RequestBuilder::PushCurrentPIDHandle() { | 188 | inline 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 | ||
| 172 | class RequestParser : public RequestHelperBase { | 204 | class RequestParser : public RequestHelperBase { |
| 173 | public: | 205 | public: |
| 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 | ||
| 316 | template <typename... H> | 404 | template <unsigned int N> |
| 317 | void RequestParser::PopHandles(H&... handles) { | 405 | std::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 | |||
| 419 | inline Kernel::SharedPtr<Kernel::Object> RequestParser::PopGenericObject() { | ||
| 420 | Kernel::Handle handle = PopHandle(); | ||
| 421 | return context->GetIncomingHandle(handle); | ||
| 422 | } | ||
| 423 | |||
| 424 | template <typename T> | ||
| 425 | Kernel::SharedPtr<T> RequestParser::PopObject() { | ||
| 426 | return Kernel::DynamicObjectCast<T>(PopGenericObject()); | ||
| 427 | } | ||
| 428 | |||
| 429 | template <unsigned int N> | ||
| 430 | inline 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 | |||
| 439 | namespace detail { | ||
| 440 | template <typename... T, size_t... I> | ||
| 441 | std::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 | |||
| 448 | template <typename... T> | ||
| 449 | inline 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 | ||
| 327 | inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) { | 454 | 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 @@ | |||
| 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 | ||
| 12 | namespace Kernel { | 14 | namespace Kernel { |
| @@ -23,4 +25,101 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | |||
| 23 | 25 | ||
| 24 | HLERequestContext::~HLERequestContext() = default; | 26 | HLERequestContext::~HLERequestContext() = default; |
| 25 | 27 | ||
| 28 | SharedPtr<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 | |||
| 33 | u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | ||
| 34 | request_handles.push_back(std::move(object)); | ||
| 35 | return request_handles.size() - 1; | ||
| 36 | } | ||
| 37 | |||
| 38 | void HLERequestContext::ClearIncomingObjects() { | ||
| 39 | request_handles.clear(); | ||
| 40 | } | ||
| 41 | |||
| 42 | ResultCode 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 | |||
| 87 | ResultCode 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 | ||
| 16 | namespace Kernel { | 21 | namespace Kernel { |
| 17 | 22 | ||
| 23 | class HandleTable; | ||
| 24 | class 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 | */ |
| 63 | class HLERequestContext { | 85 | class HLERequestContext { |
| 64 | public: | 86 | public: |
| 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 | |||
| 80 | private: | 121 | private: |
| 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/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 | |||
| 160 | void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) { | 164 | void 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 | */ |
| 32 | void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { | 34 | void 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 | */ |
| 55 | void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { | 60 | void 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 | */ |
| 79 | void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { | 83 | void 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 | */ |
| 129 | void SRV::Subscribe(Kernel::HLERequestContext& ctx) { | 139 | void 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 | */ |
| 148 | void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { | 157 | void 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 | */ |
| 168 | void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { | 176 | void 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_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 { | |||
| 26 | DistanceAttenuation = 16, | 26 | DistanceAttenuation = 16, |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | static LightingSampler SpotlightAttenuationSampler(unsigned index) { | ||
| 30 | return static_cast<LightingSampler>( | ||
| 31 | static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); | ||
| 32 | } | ||
| 33 | |||
| 34 | static LightingSampler DistanceAttenuationSampler(unsigned index) { | ||
| 35 | return static_cast<LightingSampler>( | ||
| 36 | static_cast<unsigned>(LightingSampler::DistanceAttenuation) + index); | ||
| 37 | } | ||
| 38 | |||
| 29 | /** | 39 | /** |
| 30 | * Pica fragment lighting supports using different LUTs for each lighting component: Reflectance | 40 | * Pica fragment lighting supports using different LUTs for each lighting component: Reflectance |
| 31 | * R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor, | 41 | * R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor, |
| @@ -73,6 +83,8 @@ struct LightingRegs { | |||
| 73 | VH = 1, // Cosine of the angle between the view and half-angle vectors | 83 | VH = 1, // Cosine of the angle between the view and half-angle vectors |
| 74 | NV = 2, // Cosine of the angle between the normal and the view vector | 84 | NV = 2, // Cosine of the angle between the normal and the view vector |
| 75 | LN = 3, // Cosine of the angle between the light and the normal vectors | 85 | LN = 3, // Cosine of the angle between the light and the normal vectors |
| 86 | SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors | ||
| 87 | CP = 5, // TODO: document and implement | ||
| 76 | }; | 88 | }; |
| 77 | 89 | ||
| 78 | enum class LightingBumpMode : u32 { | 90 | enum class LightingBumpMode : u32 { |
| @@ -104,6 +116,9 @@ struct LightingRegs { | |||
| 104 | return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) && | 116 | return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) && |
| 105 | (config != LightingConfig::Config5); | 117 | (config != LightingConfig::Config5); |
| 106 | 118 | ||
| 119 | case LightingSampler::SpotlightAttenuation: | ||
| 120 | return (config != LightingConfig::Config2) && (config != LightingConfig::Config3); | ||
| 121 | |||
| 107 | case LightingSampler::Fresnel: | 122 | case LightingSampler::Fresnel: |
| 108 | return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) && | 123 | return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) && |
| 109 | (config != LightingConfig::Config4); | 124 | (config != LightingConfig::Config4); |
| @@ -116,11 +131,10 @@ struct LightingRegs { | |||
| 116 | return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) || | 131 | return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) || |
| 117 | (config == LightingConfig::Config7); | 132 | (config == LightingConfig::Config7); |
| 118 | default: | 133 | default: |
| 119 | UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached " | 134 | UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached unreachable section, " |
| 120 | "unreachable section, sampler should be one " | 135 | "sampler should be one of Distribution0, Distribution1, " |
| 121 | "of Distribution0, Distribution1, Fresnel, " | 136 | "SpotlightAttenuation, Fresnel, ReflectRed, ReflectGreen or " |
| 122 | "ReflectRed, ReflectGreen or ReflectBlue, instead " | 137 | "ReflectBlue, instead got %i", |
| 123 | "got %i", | ||
| 124 | static_cast<int>(config)); | 138 | static_cast<int>(config)); |
| 125 | } | 139 | } |
| 126 | } | 140 | } |
| @@ -140,7 +154,16 @@ struct LightingRegs { | |||
| 140 | BitField<0, 16, u32> z; | 154 | BitField<0, 16, u32> z; |
| 141 | }; | 155 | }; |
| 142 | 156 | ||
| 143 | INSERT_PADDING_WORDS(0x3); | 157 | // inverse spotlight direction vector, encoded as fixed1.1.11 |
| 158 | union { | ||
| 159 | BitField<0, 13, s32> spot_x; | ||
| 160 | BitField<16, 13, s32> spot_y; | ||
| 161 | }; | ||
| 162 | union { | ||
| 163 | BitField<0, 13, s32> spot_z; | ||
| 164 | }; | ||
| 165 | |||
| 166 | INSERT_PADDING_WORDS(0x1); | ||
| 144 | 167 | ||
| 145 | union { | 168 | union { |
| 146 | BitField<0, 1, u32> directional; | 169 | BitField<0, 1, u32> directional; |
| @@ -169,8 +192,16 @@ struct LightingRegs { | |||
| 169 | } config0; | 192 | } config0; |
| 170 | 193 | ||
| 171 | union { | 194 | union { |
| 195 | u32 raw; | ||
| 196 | |||
| 197 | // Each bit specifies whether spot light attenuation should be applied for the corresponding | ||
| 198 | // light. | ||
| 199 | BitField<8, 8, u32> disable_spot_atten; | ||
| 200 | |||
| 172 | BitField<16, 1, u32> disable_lut_d0; | 201 | BitField<16, 1, u32> disable_lut_d0; |
| 173 | BitField<17, 1, u32> disable_lut_d1; | 202 | BitField<17, 1, u32> disable_lut_d1; |
| 203 | // Note: by intuition, BitField<18, 1, u32> should be disable_lut_sp, but it is actually a | ||
| 204 | // dummy bit which is always set as 1. | ||
| 174 | BitField<19, 1, u32> disable_lut_fr; | 205 | BitField<19, 1, u32> disable_lut_fr; |
| 175 | BitField<20, 1, u32> disable_lut_rr; | 206 | BitField<20, 1, u32> disable_lut_rr; |
| 176 | BitField<21, 1, u32> disable_lut_rg; | 207 | BitField<21, 1, u32> disable_lut_rg; |
| @@ -178,23 +209,15 @@ struct LightingRegs { | |||
| 178 | 209 | ||
| 179 | // Each bit specifies whether distance attenuation should be applied for the corresponding | 210 | // Each bit specifies whether distance attenuation should be applied for the corresponding |
| 180 | // light. | 211 | // light. |
| 181 | BitField<24, 1, u32> disable_dist_atten_light_0; | 212 | BitField<24, 8, u32> disable_dist_atten; |
| 182 | BitField<25, 1, u32> disable_dist_atten_light_1; | ||
| 183 | BitField<26, 1, u32> disable_dist_atten_light_2; | ||
| 184 | BitField<27, 1, u32> disable_dist_atten_light_3; | ||
| 185 | BitField<28, 1, u32> disable_dist_atten_light_4; | ||
| 186 | BitField<29, 1, u32> disable_dist_atten_light_5; | ||
| 187 | BitField<30, 1, u32> disable_dist_atten_light_6; | ||
| 188 | BitField<31, 1, u32> disable_dist_atten_light_7; | ||
| 189 | } config1; | 213 | } config1; |
| 190 | 214 | ||
| 191 | bool IsDistAttenDisabled(unsigned index) const { | 215 | bool IsDistAttenDisabled(unsigned index) const { |
| 192 | const unsigned disable[] = { | 216 | return (config1.disable_dist_atten & (1 << index)) != 0; |
| 193 | config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1, | 217 | } |
| 194 | config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3, | 218 | |
| 195 | config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5, | 219 | bool IsSpotAttenDisabled(unsigned index) const { |
| 196 | config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7}; | 220 | return (config1.disable_spot_atten & (1 << index)) != 0; |
| 197 | return disable[index] != 0; | ||
| 198 | } | 221 | } |
| 199 | 222 | ||
| 200 | union { | 223 | 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) { | |||
| 738 | SyncLightPosition(7); | 738 | SyncLightPosition(7); |
| 739 | break; | 739 | break; |
| 740 | 740 | ||
| 741 | // Fragment spot lighting direction | ||
| 742 | case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_x, 0x146 + 0 * 0x10): | ||
| 743 | case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_z, 0x147 + 0 * 0x10): | ||
| 744 | SyncLightSpotDirection(0); | ||
| 745 | break; | ||
| 746 | case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_x, 0x146 + 1 * 0x10): | ||
| 747 | case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_z, 0x147 + 1 * 0x10): | ||
| 748 | SyncLightSpotDirection(1); | ||
| 749 | break; | ||
| 750 | case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_x, 0x146 + 2 * 0x10): | ||
| 751 | case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_z, 0x147 + 2 * 0x10): | ||
| 752 | SyncLightSpotDirection(2); | ||
| 753 | break; | ||
| 754 | case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_x, 0x146 + 3 * 0x10): | ||
| 755 | case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_z, 0x147 + 3 * 0x10): | ||
| 756 | SyncLightSpotDirection(3); | ||
| 757 | break; | ||
| 758 | case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_x, 0x146 + 4 * 0x10): | ||
| 759 | case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_z, 0x147 + 4 * 0x10): | ||
| 760 | SyncLightSpotDirection(4); | ||
| 761 | break; | ||
| 762 | case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_x, 0x146 + 5 * 0x10): | ||
| 763 | case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_z, 0x147 + 5 * 0x10): | ||
| 764 | SyncLightSpotDirection(5); | ||
| 765 | break; | ||
| 766 | case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_x, 0x146 + 6 * 0x10): | ||
| 767 | case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_z, 0x147 + 6 * 0x10): | ||
| 768 | SyncLightSpotDirection(6); | ||
| 769 | break; | ||
| 770 | case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_x, 0x146 + 7 * 0x10): | ||
| 771 | case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_z, 0x147 + 7 * 0x10): | ||
| 772 | SyncLightSpotDirection(7); | ||
| 773 | break; | ||
| 774 | |||
| 741 | // Fragment lighting light source config | 775 | // Fragment lighting light source config |
| 742 | case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10): | 776 | case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10): |
| 743 | case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10): | 777 | case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10): |
| @@ -1598,6 +1632,17 @@ void RasterizerOpenGL::SyncLightPosition(int light_index) { | |||
| 1598 | } | 1632 | } |
| 1599 | } | 1633 | } |
| 1600 | 1634 | ||
| 1635 | void RasterizerOpenGL::SyncLightSpotDirection(int light_index) { | ||
| 1636 | const auto& light = Pica::g_state.regs.lighting.light[light_index]; | ||
| 1637 | GLvec3 spot_direction = {light.spot_x / 2047.0f, light.spot_y / 2047.0f, | ||
| 1638 | light.spot_z / 2047.0f}; | ||
| 1639 | |||
| 1640 | if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) { | ||
| 1641 | uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; | ||
| 1642 | uniform_block_data.dirty = true; | ||
| 1643 | } | ||
| 1644 | } | ||
| 1645 | |||
| 1601 | void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) { | 1646 | void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) { |
| 1602 | GLfloat dist_atten_bias = | 1647 | GLfloat dist_atten_bias = |
| 1603 | Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias) | 1648 | 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: | |||
| 125 | alignas(16) GLvec3 diffuse; | 125 | alignas(16) GLvec3 diffuse; |
| 126 | alignas(16) GLvec3 ambient; | 126 | alignas(16) GLvec3 ambient; |
| 127 | alignas(16) GLvec3 position; | 127 | alignas(16) GLvec3 position; |
| 128 | alignas(16) GLvec3 spot_direction; // negated | ||
| 128 | GLfloat dist_atten_bias; | 129 | GLfloat dist_atten_bias; |
| 129 | GLfloat dist_atten_scale; | 130 | GLfloat dist_atten_scale; |
| 130 | }; | 131 | }; |
| @@ -153,7 +154,7 @@ private: | |||
| 153 | }; | 154 | }; |
| 154 | 155 | ||
| 155 | static_assert( | 156 | static_assert( |
| 156 | sizeof(UniformData) == 0x3E0, | 157 | sizeof(UniformData) == 0x460, |
| 157 | "The size of the UniformData structure has changed, update the structure in the shader"); | 158 | "The size of the UniformData structure has changed, update the structure in the shader"); |
| 158 | static_assert(sizeof(UniformData) < 16384, | 159 | static_assert(sizeof(UniformData) < 16384, |
| 159 | "UniformData structure must be less than 16kb as per the OpenGL spec"); | 160 | "UniformData structure must be less than 16kb as per the OpenGL spec"); |
| @@ -241,6 +242,9 @@ private: | |||
| 241 | /// Syncs the specified light's position to match the PICA register | 242 | /// Syncs the specified light's position to match the PICA register |
| 242 | void SyncLightPosition(int light_index); | 243 | void SyncLightPosition(int light_index); |
| 243 | 244 | ||
| 245 | /// Syncs the specified spot light direcition to match the PICA register | ||
| 246 | void SyncLightSpotDirection(int light_index); | ||
| 247 | |||
| 244 | /// Syncs the specified light's distance attenuation bias to match the PICA register | 248 | /// Syncs the specified light's distance attenuation bias to match the PICA register |
| 245 | void SyncLightDistanceAttenuationBias(int light_index); | 249 | void SyncLightDistanceAttenuationBias(int light_index); |
| 246 | 250 | ||
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) { | |||
| 75 | state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; | 75 | state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; |
| 76 | state.lighting.light[light_index].dist_atten_enable = | 76 | state.lighting.light[light_index].dist_atten_enable = |
| 77 | !regs.lighting.IsDistAttenDisabled(num); | 77 | !regs.lighting.IsDistAttenDisabled(num); |
| 78 | state.lighting.light[light_index].spot_atten_enable = | ||
| 79 | !regs.lighting.IsSpotAttenDisabled(num); | ||
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0; | 82 | state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0; |
| @@ -87,6 +89,12 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { | |||
| 87 | state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value(); | 89 | state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value(); |
| 88 | state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); | 90 | state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); |
| 89 | 91 | ||
| 92 | // this is a dummy field due to lack of the corresponding register | ||
| 93 | state.lighting.lut_sp.enable = true; | ||
| 94 | state.lighting.lut_sp.abs_input = regs.lighting.abs_lut_input.disable_sp == 0; | ||
| 95 | state.lighting.lut_sp.type = regs.lighting.lut_input.sp.Value(); | ||
| 96 | state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp); | ||
| 97 | |||
| 90 | state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0; | 98 | state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0; |
| 91 | state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0; | 99 | state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0; |
| 92 | state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); | 100 | state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); |
| @@ -509,7 +517,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 509 | out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" | 517 | out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| 510 | "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" | 518 | "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| 511 | "vec3 light_vector = vec3(0.0);\n" | 519 | "vec3 light_vector = vec3(0.0);\n" |
| 512 | "vec3 refl_value = vec3(0.0);\n"; | 520 | "vec3 refl_value = vec3(0.0);\n" |
| 521 | "vec3 spot_dir = vec3(0.0);\n;"; | ||
| 513 | 522 | ||
| 514 | // Compute fragment normals | 523 | // Compute fragment normals |
| 515 | if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { | 524 | if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { |
| @@ -560,6 +569,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 560 | index = std::string("dot(light_vector, normal)"); | 569 | index = std::string("dot(light_vector, normal)"); |
| 561 | break; | 570 | break; |
| 562 | 571 | ||
| 572 | case LightingRegs::LightingLutInput::SP: | ||
| 573 | index = std::string("dot(light_vector, spot_dir)"); | ||
| 574 | break; | ||
| 575 | |||
| 563 | default: | 576 | default: |
| 564 | LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); | 577 | LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); |
| 565 | UNIMPLEMENTED(); | 578 | UNIMPLEMENTED(); |
| @@ -596,21 +609,34 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 596 | else | 609 | else |
| 597 | out += "light_vector = normalize(" + light_src + ".position + view);\n"; | 610 | out += "light_vector = normalize(" + light_src + ".position + view);\n"; |
| 598 | 611 | ||
| 612 | out += "spot_dir = " + light_src + ".spot_direction;\n"; | ||
| 613 | |||
| 599 | // Compute dot product of light_vector and normal, adjust if lighting is one-sided or | 614 | // Compute dot product of light_vector and normal, adjust if lighting is one-sided or |
| 600 | // two-sided | 615 | // two-sided |
| 601 | std::string dot_product = light_config.two_sided_diffuse | 616 | std::string dot_product = light_config.two_sided_diffuse |
| 602 | ? "abs(dot(light_vector, normal))" | 617 | ? "abs(dot(light_vector, normal))" |
| 603 | : "max(dot(light_vector, normal), 0.0)"; | 618 | : "max(dot(light_vector, normal), 0.0)"; |
| 604 | 619 | ||
| 620 | // If enabled, compute spot light attenuation value | ||
| 621 | std::string spot_atten = "1.0"; | ||
| 622 | if (light_config.spot_atten_enable && | ||
| 623 | LightingRegs::IsLightingSamplerSupported( | ||
| 624 | lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { | ||
| 625 | std::string index = | ||
| 626 | GetLutIndex(light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); | ||
| 627 | auto sampler = LightingRegs::SpotlightAttenuationSampler(light_config.num); | ||
| 628 | spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + | ||
| 629 | GetLutValue(sampler, index) + ")"; | ||
| 630 | } | ||
| 631 | |||
| 605 | // If enabled, compute distance attenuation value | 632 | // If enabled, compute distance attenuation value |
| 606 | std::string dist_atten = "1.0"; | 633 | std::string dist_atten = "1.0"; |
| 607 | if (light_config.dist_atten_enable) { | 634 | if (light_config.dist_atten_enable) { |
| 608 | std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + | 635 | std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + |
| 609 | light_src + ".position) + " + light_src + ".dist_atten_bias)"; | 636 | light_src + ".position) + " + light_src + ".dist_atten_bias)"; |
| 610 | index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; | 637 | index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; |
| 611 | const unsigned lut_num = | 638 | auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); |
| 612 | ((unsigned)LightingRegs::LightingSampler::DistanceAttenuation + light_config.num); | 639 | dist_atten = GetLutValue(sampler, index); |
| 613 | dist_atten = GetLutValue((LightingRegs::LightingSampler)lut_num, index); | ||
| 614 | } | 640 | } |
| 615 | 641 | ||
| 616 | // If enabled, clamp specular component if lighting result is negative | 642 | // If enabled, clamp specular component if lighting result is negative |
| @@ -711,11 +737,11 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 711 | 737 | ||
| 712 | // Compute primary fragment color (diffuse lighting) function | 738 | // Compute primary fragment color (diffuse lighting) function |
| 713 | out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + | 739 | out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + |
| 714 | light_src + ".ambient) * " + dist_atten + ";\n"; | 740 | light_src + ".ambient) * " + dist_atten + " * " + spot_atten + ";\n"; |
| 715 | 741 | ||
| 716 | // Compute secondary fragment color (specular lighting) function | 742 | // Compute secondary fragment color (specular lighting) function |
| 717 | out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + | 743 | out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + |
| 718 | clamp_highlights + " * " + dist_atten + ";\n"; | 744 | clamp_highlights + " * " + dist_atten + " * " + spot_atten + ";\n"; |
| 719 | } | 745 | } |
| 720 | 746 | ||
| 721 | // Sum final lighting result | 747 | // Sum final lighting result |
| @@ -967,6 +993,7 @@ struct LightSrc { | |||
| 967 | vec3 diffuse; | 993 | vec3 diffuse; |
| 968 | vec3 ambient; | 994 | vec3 ambient; |
| 969 | vec3 position; | 995 | vec3 position; |
| 996 | vec3 spot_direction; | ||
| 970 | float dist_atten_bias; | 997 | float dist_atten_bias; |
| 971 | float dist_atten_scale; | 998 | float dist_atten_scale; |
| 972 | }; | 999 | }; |
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 { | |||
| 93 | bool directional; | 93 | bool directional; |
| 94 | bool two_sided_diffuse; | 94 | bool two_sided_diffuse; |
| 95 | bool dist_atten_enable; | 95 | bool dist_atten_enable; |
| 96 | bool spot_atten_enable; | ||
| 96 | } light[8]; | 97 | } light[8]; |
| 97 | 98 | ||
| 98 | bool enable; | 99 | bool enable; |
| @@ -110,7 +111,7 @@ union PicaShaderConfig { | |||
| 110 | bool abs_input; | 111 | bool abs_input; |
| 111 | Pica::LightingRegs::LightingLutInput type; | 112 | Pica::LightingRegs::LightingLutInput type; |
| 112 | float scale; | 113 | float scale; |
| 113 | } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb; | 114 | } lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb; |
| 114 | } lighting; | 115 | } lighting; |
| 115 | 116 | ||
| 116 | struct { | 117 | struct { |