Delay singlecore gpu interrupts
Fixes Bomberman Jetters in single core mode. When single core mode pauses the CPU to execute the GPU FIFO it greedily executes the whole thing. Before this commit, Finish and Token interrupts would happen instantly, not even taking into account how long the current FIFO window has taken to execute. The interrupts would be effectively backdated to the start of this execution window. This commit does two things: It pipes the current FIFO window execution time though to the interrupt scheduling and it enforces a minimum delay of 500 cycles before an interrupt will be fired.
This commit is contained in:
parent
0b81640dd1
commit
f5c550e9cb
|
@ -2205,7 +2205,7 @@ struct BPMemory
|
||||||
|
|
||||||
extern BPMemory bpmem;
|
extern BPMemory bpmem;
|
||||||
|
|
||||||
void LoadBPReg(u32 value0);
|
void LoadBPReg(u32 value0, int cycles_into_future);
|
||||||
void LoadBPRegPreprocess(u32 value0);
|
void LoadBPRegPreprocess(u32 value0, int cycles_into_future);
|
||||||
|
|
||||||
std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata);
|
std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata);
|
||||||
|
|
|
@ -50,7 +50,7 @@ void BPInit()
|
||||||
bpmem.bpMask = 0xFFFFFF;
|
bpmem.bpMask = 0xFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BPWritten(const BPCmd& bp)
|
static void BPWritten(const BPCmd& bp, int cycles_into_future)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
----------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -180,7 +180,7 @@ static void BPWritten(const BPCmd& bp)
|
||||||
g_texture_cache->FlushEFBCopies();
|
g_texture_cache->FlushEFBCopies();
|
||||||
g_framebuffer_manager->InvalidatePeekCache(false);
|
g_framebuffer_manager->InvalidatePeekCache(false);
|
||||||
if (!Fifo::UseDeterministicGPUThread())
|
if (!Fifo::UseDeterministicGPUThread())
|
||||||
PixelEngine::SetFinish(); // may generate interrupt
|
PixelEngine::SetFinish(cycles_into_future); // may generate interrupt
|
||||||
DEBUG_LOG_FMT(VIDEO, "GXSetDrawDone SetPEFinish (value: {:#04X})", bp.newvalue & 0xFFFF);
|
DEBUG_LOG_FMT(VIDEO, "GXSetDrawDone SetPEFinish (value: {:#04X})", bp.newvalue & 0xFFFF);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -193,14 +193,14 @@ static void BPWritten(const BPCmd& bp)
|
||||||
g_texture_cache->FlushEFBCopies();
|
g_texture_cache->FlushEFBCopies();
|
||||||
g_framebuffer_manager->InvalidatePeekCache(false);
|
g_framebuffer_manager->InvalidatePeekCache(false);
|
||||||
if (!Fifo::UseDeterministicGPUThread())
|
if (!Fifo::UseDeterministicGPUThread())
|
||||||
PixelEngine::SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), false);
|
PixelEngine::SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), false, cycles_into_future);
|
||||||
DEBUG_LOG_FMT(VIDEO, "SetPEToken {:#06X}", bp.newvalue & 0xFFFF);
|
DEBUG_LOG_FMT(VIDEO, "SetPEToken {:#06X}", bp.newvalue & 0xFFFF);
|
||||||
return;
|
return;
|
||||||
case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
|
case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
|
||||||
g_texture_cache->FlushEFBCopies();
|
g_texture_cache->FlushEFBCopies();
|
||||||
g_framebuffer_manager->InvalidatePeekCache(false);
|
g_framebuffer_manager->InvalidatePeekCache(false);
|
||||||
if (!Fifo::UseDeterministicGPUThread())
|
if (!Fifo::UseDeterministicGPUThread())
|
||||||
PixelEngine::SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), true);
|
PixelEngine::SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), true, cycles_into_future);
|
||||||
DEBUG_LOG_FMT(VIDEO, "SetPEToken + INT {:#06X}", bp.newvalue & 0xFFFF);
|
DEBUG_LOG_FMT(VIDEO, "SetPEToken + INT {:#06X}", bp.newvalue & 0xFFFF);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -717,7 +717,7 @@ static void BPWritten(const BPCmd& bp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
|
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
|
||||||
void LoadBPReg(u32 value0)
|
void LoadBPReg(u32 value0, int cycles_into_future)
|
||||||
{
|
{
|
||||||
int regNum = value0 >> 24;
|
int regNum = value0 >> 24;
|
||||||
int oldval = ((u32*)&bpmem)[regNum];
|
int oldval = ((u32*)&bpmem)[regNum];
|
||||||
|
@ -730,10 +730,10 @@ void LoadBPReg(u32 value0)
|
||||||
if (regNum != BPMEM_BP_MASK)
|
if (regNum != BPMEM_BP_MASK)
|
||||||
bpmem.bpMask = 0xFFFFFF;
|
bpmem.bpMask = 0xFFFFFF;
|
||||||
|
|
||||||
BPWritten(bp);
|
BPWritten(bp, cycles_into_future);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadBPRegPreprocess(u32 value0)
|
void LoadBPRegPreprocess(u32 value0, int cycles_into_future)
|
||||||
{
|
{
|
||||||
int regNum = value0 >> 24;
|
int regNum = value0 >> 24;
|
||||||
// masking could hypothetically be a problem
|
// masking could hypothetically be a problem
|
||||||
|
@ -742,13 +742,13 @@ void LoadBPRegPreprocess(u32 value0)
|
||||||
{
|
{
|
||||||
case BPMEM_SETDRAWDONE:
|
case BPMEM_SETDRAWDONE:
|
||||||
if ((newval & 0xff) == 0x02)
|
if ((newval & 0xff) == 0x02)
|
||||||
PixelEngine::SetFinish();
|
PixelEngine::SetFinish(cycles_into_future);
|
||||||
break;
|
break;
|
||||||
case BPMEM_PE_TOKEN_ID:
|
case BPMEM_PE_TOKEN_ID:
|
||||||
PixelEngine::SetToken(newval & 0xffff, false);
|
PixelEngine::SetToken(newval & 0xffff, false, cycles_into_future);
|
||||||
break;
|
break;
|
||||||
case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
|
case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
|
||||||
PixelEngine::SetToken(newval & 0xffff, true);
|
PixelEngine::SetToken(newval & 0xffff, true, cycles_into_future);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,11 +221,11 @@ u8* Run(DataReader src, u32* cycles, bool in_display_list)
|
||||||
const u32 bp_cmd = src.Read<u32>();
|
const u32 bp_cmd = src.Read<u32>();
|
||||||
if constexpr (is_preprocess)
|
if constexpr (is_preprocess)
|
||||||
{
|
{
|
||||||
LoadBPRegPreprocess(bp_cmd);
|
LoadBPRegPreprocess(bp_cmd, total_cycles);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LoadBPReg(bp_cmd);
|
LoadBPReg(bp_cmd, total_cycles);
|
||||||
INCSTAT(g_stats.this_frame.num_bp_loads);
|
INCSTAT(g_stats.this_frame.num_bp_loads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ static void SetTokenFinish_OnMainThread(u64 userdata, s64 cyclesLate)
|
||||||
// Raise the event handler above on the CPU thread.
|
// Raise the event handler above on the CPU thread.
|
||||||
// s_token_finish_mutex must be locked.
|
// s_token_finish_mutex must be locked.
|
||||||
// THIS IS EXECUTED FROM VIDEO THREAD
|
// THIS IS EXECUTED FROM VIDEO THREAD
|
||||||
static void RaiseEvent()
|
static void RaiseEvent(int cycles_into_future)
|
||||||
{
|
{
|
||||||
if (s_event_raised)
|
if (s_event_raised)
|
||||||
return;
|
return;
|
||||||
|
@ -285,14 +285,21 @@ static void RaiseEvent()
|
||||||
s_event_raised = true;
|
s_event_raised = true;
|
||||||
|
|
||||||
CoreTiming::FromThread from = CoreTiming::FromThread::NON_CPU;
|
CoreTiming::FromThread from = CoreTiming::FromThread::NON_CPU;
|
||||||
|
s64 cycles = 0; // we don't care about timings for dual core mode.
|
||||||
if (!SConfig::GetInstance().bCPUThread || Fifo::UseDeterministicGPUThread())
|
if (!SConfig::GetInstance().bCPUThread || Fifo::UseDeterministicGPUThread())
|
||||||
|
{
|
||||||
from = CoreTiming::FromThread::CPU;
|
from = CoreTiming::FromThread::CPU;
|
||||||
CoreTiming::ScheduleEvent(0, et_SetTokenFinishOnMainThread, 0, from);
|
|
||||||
|
// Hack: Dolphin's single-core gpu timings are way too fast. Enforce a minimum delay to give
|
||||||
|
// games time to setup any interrupt state
|
||||||
|
cycles = std::max(500, cycles_into_future);
|
||||||
|
}
|
||||||
|
CoreTiming::ScheduleEvent(cycles, et_SetTokenFinishOnMainThread, 0, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetToken
|
// SetToken
|
||||||
// THIS IS EXECUTED FROM VIDEO THREAD
|
// THIS IS EXECUTED FROM VIDEO THREAD
|
||||||
void SetToken(const u16 token, const bool interrupt)
|
void SetToken(const u16 token, const bool interrupt, int cycles_into_future)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: {:04x})", token);
|
DEBUG_LOG_FMT(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: {:04x})", token);
|
||||||
|
|
||||||
|
@ -301,12 +308,12 @@ void SetToken(const u16 token, const bool interrupt)
|
||||||
s_token_pending = token;
|
s_token_pending = token;
|
||||||
s_token_interrupt_pending |= interrupt;
|
s_token_interrupt_pending |= interrupt;
|
||||||
|
|
||||||
RaiseEvent();
|
RaiseEvent(cycles_into_future);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFinish
|
// SetFinish
|
||||||
// THIS IS EXECUTED FROM VIDEO THREAD (BPStructs.cpp) when a new frame has been drawn
|
// THIS IS EXECUTED FROM VIDEO THREAD (BPStructs.cpp) when a new frame has been drawn
|
||||||
void SetFinish()
|
void SetFinish(int cycles_into_future)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(PIXELENGINE, "VIDEO Set Finish");
|
DEBUG_LOG_FMT(PIXELENGINE, "VIDEO Set Finish");
|
||||||
|
|
||||||
|
@ -314,7 +321,7 @@ void SetFinish()
|
||||||
|
|
||||||
s_finish_interrupt_pending |= true;
|
s_finish_interrupt_pending |= true;
|
||||||
|
|
||||||
RaiseEvent();
|
RaiseEvent(cycles_into_future);
|
||||||
}
|
}
|
||||||
|
|
||||||
UPEAlphaReadReg GetAlphaReadMode()
|
UPEAlphaReadReg GetAlphaReadMode()
|
||||||
|
|
|
@ -61,8 +61,8 @@ void DoState(PointerWrap& p);
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||||
|
|
||||||
// gfx backend support
|
// gfx backend support
|
||||||
void SetToken(const u16 token, const bool interrupt);
|
void SetToken(const u16 token, const bool interrupt, int cycle_delay);
|
||||||
void SetFinish();
|
void SetFinish(int cycle_delay);
|
||||||
UPEAlphaReadReg GetAlphaReadMode();
|
UPEAlphaReadReg GetAlphaReadMode();
|
||||||
|
|
||||||
} // namespace PixelEngine
|
} // namespace PixelEngine
|
||||||
|
|
Loading…
Reference in New Issue