From aabb07cca45f936a0acc159e0632e0395f1faf85 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 11:24:12 -0500 Subject: [PATCH 1/8] HLE/GSP: Make RegisterInterruptRelayQueue work in a per-session basis. The registered interrupt event is unique to each session that calls RegisterInterruptRelayQueue, and only that event should be reset when UnregisterInterruptRelayQueue is called. --- src/core/hle/service/gsp/gsp_gpu.cpp | 55 +++++++++++++++++++--------- src/core/hle/service/gsp/gsp_gpu.h | 25 +++++++++---- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index bb3cedbe3a..4e854164e8 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -9,7 +9,6 @@ #include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" @@ -319,12 +318,19 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x13, 1, 2); u32 flags = rp.Pop(); - interrupt_event = rp.PopObject(); + auto interrupt_event = rp.PopObject(); // TODO(mailwl): return right error code instead assert ASSERT_MSG((interrupt_event != nullptr), "handle is not valid!"); interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; + u32 thread_id = next_thread_id++; + + SessionData* session_data = GetSessionData(ctx.Session()); + session_data->thread_id = thread_id; + session_data->interrupt_event = std::move(interrupt_event); + session_data->registered = true; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); if (first_initialization) { @@ -338,22 +344,23 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { rb.Push(thread_id); rb.PushCopyObjects(shared_memory); - thread_id++; - interrupt_event->Signal(); // TODO(bunnei): Is this correct? - - LOG_WARNING(Service_GSP, "called, flags=0x%08X", flags); + LOG_DEBUG(Service_GSP, "called, flags=0x%08X", flags); } void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x14, 0, 0); - thread_id = 0; - interrupt_event = nullptr; + SessionData* session_data = GetSessionData(ctx.Session()); + session_data->thread_id = 0; + session_data->interrupt_event = nullptr; + session_data->registered = false; + + // TODO(Subv): Reset next_thread_id so that it doesn't go past the maximum of 4. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_GSP, "(STUBBED) called"); + LOG_DEBUG(Service_GSP, "called"); } /** @@ -366,15 +373,20 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { if (!gpu_right_acquired) { return; } - if (nullptr == interrupt_event) { - LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); - return; - } if (nullptr == shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; } for (int thread_id = 0; thread_id < 0x4; ++thread_id) { + SessionData* session_data = FindRegisteredThreadData(thread_id); + if (session_data == nullptr) + continue; + + auto interrupt_event = session_data->interrupt_event; + if (interrupt_event == nullptr) { + LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); + continue; + } InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(shared_memory, thread_id); u8 next = interrupt_relay_queue->index; @@ -398,8 +410,8 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { info->is_dirty.Assign(false); } } + interrupt_event->Signal(); } - interrupt_event->Signal(); } MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); @@ -655,6 +667,17 @@ void GSP_GPU::StoreDataCache(Kernel::HLERequestContext& ctx) { size, process->process_id); } +SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) { + for (auto& session_info : connected_sessions) { + SessionData* data = static_cast(session_info.data.get()) + if (!data->registered) + continue; + if (data->thread_id == thread_id) + return data; + } + return nullptr; +} + GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { static const FunctionInfo functions[] = { {0x00010082, &GSP_GPU::WriteHWRegs, "WriteHWRegs"}, @@ -691,17 +714,13 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { }; RegisterHandlers(functions); - interrupt_event = nullptr; - using Kernel::MemoryPermission; shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); - thread_id = 0; gpu_right_acquired = false; first_initialization = true; }; - } // namespace GSP } // namespace Service diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 98756a8ff3..737525653f 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -8,12 +8,12 @@ #include #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/result.h" #include "core/hle/service/service.h" namespace Kernel { -class Event; class SharedMemory; } // namespace Kernel @@ -179,7 +179,16 @@ struct CommandBuffer { }; static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); -class GSP_GPU final : public ServiceFramework { +struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { + /// Event triggered when GSP interrupt has been signalled + Kernel::SharedPtr interrupt_event; + /// Thread index into interrupt relay queue + u32 thread_id = 0; + /// Whether RegisterInterruptRelayQueue was called for this session + bool registered = false; +}; + +class GSP_GPU final : public ServiceFramework { public: GSP_GPU(); ~GSP_GPU() = default; @@ -351,12 +360,14 @@ private: */ void StoreDataCache(Kernel::HLERequestContext& ctx); - /// Event triggered when GSP interrupt has been signalled - Kernel::SharedPtr interrupt_event; - /// GSP shared memoryings + /// Returns the session data for the specified registered thread id, or nullptr if not found. + SessionData* FindRegisteredThreadData(u32 thread_id); + + /// Next threadid value to use when RegisterInterruptRelayQueue is called. + u32 next_thread_id = 0; + + /// GSP shared memory Kernel::SharedPtr shared_memory; - /// Thread index into interrupt relay queue - u32 thread_id = 0; bool gpu_right_acquired = false; bool first_initialization = true; From 05a44ed353853a8b693cb5f627ca41c6c045791b Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 11:43:30 -0500 Subject: [PATCH 2/8] HLE/GSP: Keep track of the thread that currently has the GPU right. This thread is the only one for which the GSP interrupts should be signaled, except for the PDC0/1 interrupts. --- src/core/hle/service/gsp/gsp_gpu.cpp | 20 ++++++++++++-------- src/core/hle/service/gsp/gsp_gpu.h | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 4e854164e8..15e8de3224 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -370,9 +370,6 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { * @todo This probably does not belong in the GSP module, instead move to video_core */ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { - if (!gpu_right_acquired) { - return; - } if (nullptr == shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; @@ -634,18 +631,26 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { u32 flag = rp.Pop(); auto process = rp.PopObject(); - gpu_right_acquired = true; + // TODO(Subv): This case should put the caller thread to sleep until the right is released. + ASSERT_MSG(active_thread_id == -1, "GPU right has already been acquired"); + + SessionData* session_data = GetSessionData(ctx.Session()); + active_thread_id = session_data->thread_id; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_GSP, "called flag=%08X process=%u", flag, process->process_id); + LOG_WARNING(Service_GSP, "called flag=%08X process=%u thread_id=%u", flag, process->process_id, + active_thread_id); } void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x17, 0, 0); - gpu_right_acquired = false; + SessionData* session_data = GetSessionData(ctx.Session()); + ASSERT_MSG(active_thread_id == session_data->thread_id, + "Wrong thread tried to release GPU right"); + active_thread_id = -1; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -669,7 +674,7 @@ void GSP_GPU::StoreDataCache(Kernel::HLERequestContext& ctx) { SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) { for (auto& session_info : connected_sessions) { - SessionData* data = static_cast(session_info.data.get()) + SessionData* data = static_cast(session_info.data.get()); if (!data->registered) continue; if (data->thread_id == thread_id) @@ -719,7 +724,6 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); - gpu_right_acquired = false; first_initialization = true; }; } // namespace GSP diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 737525653f..01bab86522 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -369,7 +369,9 @@ private: /// GSP shared memory Kernel::SharedPtr shared_memory; - bool gpu_right_acquired = false; + /// Thread id that currently has GPU rights or -1 if none. + int active_thread_id = -1; + bool first_initialization = true; }; From 68fc3b36e635bebd071cb78b38d28b0424553af9 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 11:55:44 -0500 Subject: [PATCH 3/8] HLE/GSP: Only trigger GSP interrupts for the current active GSP thread. This is true for all interrupts except PDC0 and PDC1, which should be triggered for all registered threads. TODO: The real GSP module seems to only trigger PDC0 after updating the screens (both top and bottom). PDC1 doesn't seem to be triggered at all. --- src/core/hle/service/gsp/gsp_gpu.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 15e8de3224..fb4343e7bc 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -50,6 +50,9 @@ constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorM ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC +/// Maximum number of threads that can be registered at the same time in the GSP module. +constexpr u32 MaxGSPThreads = 4; + /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(Kernel::SharedPtr shared_memory, u32 thread_id) { @@ -325,6 +328,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; u32 thread_id = next_thread_id++; + ASSERT_MSG(thread_id < MaxGSPThreads, "GSP thread id overflow"); SessionData* session_data = GetSessionData(ctx.Session()); session_data->thread_id = thread_id; @@ -370,11 +374,23 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { * @todo This probably does not belong in the GSP module, instead move to video_core */ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { + // Don't do anything if no process has acquired the GPU right. + if (active_thread_id == -1) + return; + if (nullptr == shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; } - for (int thread_id = 0; thread_id < 0x4; ++thread_id) { + + // Normal interrupts are only signaled for the active thread (ie, the thread that has the GPU + // right), but the PDC0/1 interrupts are signaled for every registered thread. + for (int thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { + if (interrupt_id != InterruptId::PDC0 && interrupt_id != InterruptId::PDC1) { + // Ignore threads that aren't the current active thread + if (thread_id != active_thread_id) + continue; + } SessionData* session_data = FindRegisteredThreadData(thread_id); if (session_data == nullptr) continue; @@ -398,6 +414,8 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { // Update framebuffer information if requested // TODO(yuriks): Confirm where this code should be called. It is definitely updated without // executing any GSP commands, only waiting on the event. + // TODO(Subv): The real GSP module triggers PDC0 after updating both the top and bottom + // screen, it is currently unknown what PDC1 does. int screen_id = (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; if (screen_id != -1) { From fbef978b984bc80a6f4159fd143bde1c0c7e34b7 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 15:09:03 -0500 Subject: [PATCH 4/8] GSP: Return the correct result code if AcquireRight is called multiple times from the same thread. --- src/core/hle/service/gsp/gsp_gpu.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index fb4343e7bc..11f9f4ae61 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -649,17 +649,25 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { u32 flag = rp.Pop(); auto process = rp.PopObject(); + SessionData* session_data = GetSessionData(ctx.Session()); + + LOG_WARNING(Service_GSP, "called flag=%08X process=%u thread_id=%u", flag, process->process_id, + session_data->thread_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + if (active_thread_id == session_data->thread_id) { + rb.Push(ResultCode(ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success, + ErrorLevel::Success)); + return; + } + // TODO(Subv): This case should put the caller thread to sleep until the right is released. ASSERT_MSG(active_thread_id == -1, "GPU right has already been acquired"); - SessionData* session_data = GetSessionData(ctx.Session()); active_thread_id = session_data->thread_id; - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_GSP, "called flag=%08X process=%u thread_id=%u", flag, process->process_id, - active_thread_id); } void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) { From d17f148e48afe5c43673da4dbb9ee45c640fa52f Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 2 Jan 2018 12:07:26 -0500 Subject: [PATCH 5/8] Services/GSP: Assign a thread id to each connected session when the session is created. Most applications call AcquireRight before calling RegisterInterruptRelayQueue so we can't assign the thread id there. This fixes the bug with LLE applets not launching properly. --- src/core/hle/service/gsp/gsp_gpu.cpp | 26 +++++++++++++++++++++----- src/core/hle/service/gsp/gsp_gpu.h | 7 +++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 11f9f4ae61..7156f3f5ce 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -53,6 +53,17 @@ constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorM /// Maximum number of threads that can be registered at the same time in the GSP module. constexpr u32 MaxGSPThreads = 4; +/// Thread ids currently in use by the sessions connected to the GSPGPU service. +static std::array used_thread_ids = {false, false, false, false}; + +static u32 GetUnusedThreadId() { + for (u32 id = 0; id < MaxGSPThreads; ++id) { + if (!used_thread_ids[id]) + return id; + } + ASSERT_MSG(false, "All GSP threads are in use"); +} + /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(Kernel::SharedPtr shared_memory, u32 thread_id) { @@ -327,11 +338,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; - u32 thread_id = next_thread_id++; - ASSERT_MSG(thread_id < MaxGSPThreads, "GSP thread id overflow"); - SessionData* session_data = GetSessionData(ctx.Session()); - session_data->thread_id = thread_id; session_data->interrupt_event = std::move(interrupt_event); session_data->registered = true; @@ -345,7 +352,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } - rb.Push(thread_id); + rb.Push(session_data->thread_id); rb.PushCopyObjects(shared_memory); LOG_DEBUG(Service_GSP, "called, flags=0x%08X", flags); @@ -752,5 +759,14 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { first_initialization = true; }; + +SessionData::SessionData() { + // Assign a new thread id to this session when it connects. Note: In the real GSP service this + // is done through a real thread (svcCreateThread) but we have to simulate it since our HLE + // services don't have threads. + thread_id = GetUnusedThreadId(); + used_thread_ids[thread_id] = true; +} + } // namespace GSP } // namespace Service diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 01bab86522..c057dffc42 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -180,10 +180,12 @@ struct CommandBuffer { static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { + SessionData(); + /// Event triggered when GSP interrupt has been signalled Kernel::SharedPtr interrupt_event; /// Thread index into interrupt relay queue - u32 thread_id = 0; + u32 thread_id; /// Whether RegisterInterruptRelayQueue was called for this session bool registered = false; }; @@ -363,9 +365,6 @@ private: /// Returns the session data for the specified registered thread id, or nullptr if not found. SessionData* FindRegisteredThreadData(u32 thread_id); - /// Next threadid value to use when RegisterInterruptRelayQueue is called. - u32 next_thread_id = 0; - /// GSP shared memory Kernel::SharedPtr shared_memory; From 75f68c48605aa09e183b3a2c390b43aa9a0d5d0e Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 2 Jan 2018 12:09:43 -0500 Subject: [PATCH 6/8] Services/GSP: Mark the thread ids as unused when a GSP session is destroyed. This fixes the games that call RegisterInterruptRelayQueue and UnregisterInterruptRelayQueue frequently. --- src/core/hle/service/gsp/gsp_gpu.cpp | 8 +++++--- src/core/hle/service/gsp/gsp_gpu.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 7156f3f5ce..41de8f5619 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -362,12 +362,9 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x14, 0, 0); SessionData* session_data = GetSessionData(ctx.Session()); - session_data->thread_id = 0; session_data->interrupt_event = nullptr; session_data->registered = false; - // TODO(Subv): Reset next_thread_id so that it doesn't go past the maximum of 4. - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -768,5 +765,10 @@ SessionData::SessionData() { used_thread_ids[thread_id] = true; } +SessionData::~SessionData() { + // Free the thread id slot so that other sessions can use it. + used_thread_ids[thread_id] = false; +} + } // namespace GSP } // namespace Service diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index c057dffc42..55c2343a44 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -181,6 +181,7 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { SessionData(); + ~SessionData(); /// Event triggered when GSP interrupt has been signalled Kernel::SharedPtr interrupt_event; From aa90198ec5e1bab2a7b875611ac07ac6da05fe6e Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 3 Jan 2018 10:52:46 -0500 Subject: [PATCH 7/8] Services: Make SessionDataBase's destructor virtual. --- src/core/hle/kernel/hle_ipc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index ccff81a21a..95d9624db0 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -59,7 +59,9 @@ public: /// Empty placeholder structure for services with no per-session data. The session data classes /// in each service must inherit from this. - struct SessionDataBase {}; + struct SessionDataBase { + virtual ~SessionDataBase() = default; + }; protected: /// Creates the storage for the session data of the service. From 34685f48dce91a12d0c7b0e014c6ce9ada7177e7 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 6 Jan 2018 13:51:33 -0500 Subject: [PATCH 8/8] GSP: Allow the signaling of the PDC0/1 interrupts even if the GPU right hasn't been acquired. This was verified with a hwtest. --- src/core/hle/service/gsp/gsp_gpu.cpp | 92 +++++++++++++++------------- src/core/hle/service/gsp/gsp_gpu.h | 8 +++ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 41de8f5619..3a919e87ea 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -371,6 +371,43 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_GSP, "called"); } +void GSP_GPU::SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id) { + SessionData* session_data = FindRegisteredThreadData(thread_id); + if (session_data == nullptr) + return; + + auto interrupt_event = session_data->interrupt_event; + if (interrupt_event == nullptr) { + LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); + return; + } + InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(shared_memory, thread_id); + u8 next = interrupt_relay_queue->index; + next += interrupt_relay_queue->number_interrupts; + next = next % 0x34; // 0x34 is the number of interrupt slots + + interrupt_relay_queue->number_interrupts += 1; + + interrupt_relay_queue->slot[next] = interrupt_id; + interrupt_relay_queue->error_code = 0x0; // No error + + // Update framebuffer information if requested + // TODO(yuriks): Confirm where this code should be called. It is definitely updated without + // executing any GSP commands, only waiting on the event. + // TODO(Subv): The real GSP module triggers PDC0 after updating both the top and bottom + // screen, it is currently unknown what PDC1 does. + int screen_id = + (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; + if (screen_id != -1) { + FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); + if (info->is_dirty) { + GSP::SetBufferSwap(screen_id, info->framebuffer_info[info->index]); + info->is_dirty.Assign(false); + } + } + interrupt_event->Signal(); +} + /** * Signals that the specified interrupt type has occurred to userland code * @param interrupt_id ID of interrupt that is being signalled @@ -378,59 +415,26 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { * @todo This probably does not belong in the GSP module, instead move to video_core */ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { - // Don't do anything if no process has acquired the GPU right. - if (active_thread_id == -1) - return; - if (nullptr == shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; } + // The PDC0 and PDC1 interrupts are fired even if the GPU right hasn't been acquired. // Normal interrupts are only signaled for the active thread (ie, the thread that has the GPU // right), but the PDC0/1 interrupts are signaled for every registered thread. - for (int thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { - if (interrupt_id != InterruptId::PDC0 && interrupt_id != InterruptId::PDC1) { - // Ignore threads that aren't the current active thread - if (thread_id != active_thread_id) - continue; + if (interrupt_id == InterruptId::PDC0 || interrupt_id == InterruptId::PDC1) { + for (u32 thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { + SignalInterruptForThread(interrupt_id, thread_id); } - SessionData* session_data = FindRegisteredThreadData(thread_id); - if (session_data == nullptr) - continue; - - auto interrupt_event = session_data->interrupt_event; - if (interrupt_event == nullptr) { - LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); - continue; - } - InterruptRelayQueue* interrupt_relay_queue = - GetInterruptRelayQueue(shared_memory, thread_id); - u8 next = interrupt_relay_queue->index; - next += interrupt_relay_queue->number_interrupts; - next = next % 0x34; // 0x34 is the number of interrupt slots - - interrupt_relay_queue->number_interrupts += 1; - - interrupt_relay_queue->slot[next] = interrupt_id; - interrupt_relay_queue->error_code = 0x0; // No error - - // Update framebuffer information if requested - // TODO(yuriks): Confirm where this code should be called. It is definitely updated without - // executing any GSP commands, only waiting on the event. - // TODO(Subv): The real GSP module triggers PDC0 after updating both the top and bottom - // screen, it is currently unknown what PDC1 does. - int screen_id = - (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; - if (screen_id != -1) { - FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); - if (info->is_dirty) { - GSP::SetBufferSwap(screen_id, info->framebuffer_info[info->index]); - info->is_dirty.Assign(false); - } - } - interrupt_event->Signal(); + return; } + + // For normal interrupts, don't do anything if no process has acquired the GPU right. + if (active_thread_id == -1) + return; + + SignalInterruptForThread(interrupt_id, active_thread_id); } MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 55c2343a44..214d96c0ef 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -213,6 +213,14 @@ public: FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); private: + /** + * Signals that the specified interrupt type has occurred to userland code for the specified GSP + * thread id. + * @param interrupt_id ID of interrupt that is being signalled. + * @param thread_id GSP thread that will receive the interrupt. + */ + void SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id); + /** * GSP_GPU::WriteHWRegs service function *