citra-emu
/
citra-canary
Archived
1
0
Fork 0

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.
This commit is contained in:
Subv 2017-12-21 11:55:44 -05:00
parent 05a44ed353
commit 68fc3b36e6
1 changed files with 19 additions and 1 deletions

View File

@ -50,6 +50,9 @@ constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorM
ErrorSummary::InvalidArgument, ErrorSummary::InvalidArgument,
ErrorLevel::Usage); // 0xE0E02BEC 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 /// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory, static inline u8* GetCommandBuffer(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory,
u32 thread_id) { u32 thread_id) {
@ -325,6 +328,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; interrupt_event->name = "GSP_GSP_GPU::interrupt_event";
u32 thread_id = next_thread_id++; u32 thread_id = next_thread_id++;
ASSERT_MSG(thread_id < MaxGSPThreads, "GSP thread id overflow");
SessionData* session_data = GetSessionData(ctx.Session()); SessionData* session_data = GetSessionData(ctx.Session());
session_data->thread_id = thread_id; 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 * @todo This probably does not belong in the GSP module, instead move to video_core
*/ */
void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { 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) { if (nullptr == shared_memory) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
return; 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); SessionData* session_data = FindRegisteredThreadData(thread_id);
if (session_data == nullptr) if (session_data == nullptr)
continue; continue;
@ -398,6 +414,8 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) {
// Update framebuffer information if requested // Update framebuffer information if requested
// TODO(yuriks): Confirm where this code should be called. It is definitely updated without // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
// executing any GSP commands, only waiting on the event. // 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 = int screen_id =
(interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1;
if (screen_id != -1) { if (screen_id != -1) {