mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	GSP: Allow the signaling of the PDC0/1 interrupts even if the GPU right hasn't been acquired.
This was verified with a hwtest.
This commit is contained in:
		
							parent
							
								
									aa90198ec5
								
							
						
					
					
						commit
						34685f48dc
					
				
					 2 changed files with 56 additions and 44 deletions
				
			
		|  | @ -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)); | ||||
|  |  | |||
|  | @ -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 | ||||
|      * | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue