Skip to content

Commit

Permalink
Delay singlecore gpu interrupts
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
phire committed Nov 24, 2021
1 parent 0b81640 commit 263dd01
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 23 deletions.
4 changes: 2 additions & 2 deletions Source/Core/VideoCommon/BPMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -2205,7 +2205,7 @@ struct BPMemory

extern BPMemory bpmem;

void LoadBPReg(u32 value0);
void LoadBPRegPreprocess(u32 value0);
void LoadBPReg(u32 value0, int cycles_into_future);
void LoadBPRegPreprocess(u32 value0, int cycles_into_future);

std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata);
20 changes: 10 additions & 10 deletions Source/Core/VideoCommon/BPStructs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void BPInit()
bpmem.bpMask = 0xFFFFFF;
}

static void BPWritten(const BPCmd& bp)
static void BPWritten(const BPCmd& bp, int cycles_into_future)
{
/*
----------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -180,7 +180,7 @@ static void BPWritten(const BPCmd& bp)
g_texture_cache->FlushEFBCopies();
g_framebuffer_manager->InvalidatePeekCache(false);
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);
return;

Expand All @@ -193,14 +193,14 @@ static void BPWritten(const BPCmd& bp)
g_texture_cache->FlushEFBCopies();
g_framebuffer_manager->InvalidatePeekCache(false);
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);
return;
case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
g_texture_cache->FlushEFBCopies();
g_framebuffer_manager->InvalidatePeekCache(false);
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);
return;

Expand Down Expand Up @@ -717,7 +717,7 @@ static void BPWritten(const BPCmd& bp)
}

// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
void LoadBPReg(u32 value0)
void LoadBPReg(u32 value0, int cycles_into_future)
{
int regNum = value0 >> 24;
int oldval = ((u32*)&bpmem)[regNum];
Expand All @@ -730,10 +730,10 @@ void LoadBPReg(u32 value0)
if (regNum != BPMEM_BP_MASK)
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;
// masking could hypothetically be a problem
Expand All @@ -742,13 +742,13 @@ void LoadBPRegPreprocess(u32 value0)
{
case BPMEM_SETDRAWDONE:
if ((newval & 0xff) == 0x02)
PixelEngine::SetFinish();
PixelEngine::SetFinish(cycles_into_future);
break;
case BPMEM_PE_TOKEN_ID:
PixelEngine::SetToken(newval & 0xffff, false);
PixelEngine::SetToken(newval & 0xffff, false, cycles_into_future);
break;
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;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/VideoCommon/OpcodeDecoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ u8* Run(DataReader src, u32* cycles, bool in_display_list)
const u32 bp_cmd = src.Read<u32>();
if constexpr (is_preprocess)
{
LoadBPRegPreprocess(bp_cmd);
LoadBPRegPreprocess(bp_cmd, total_cycles);
}
else
{
LoadBPReg(bp_cmd);
LoadBPReg(bp_cmd, total_cycles);
INCSTAT(g_stats.this_frame.num_bp_loads);
}
}
Expand Down
20 changes: 13 additions & 7 deletions Source/Core/VideoCommon/PixelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,22 +277,28 @@ static void SetTokenFinish_OnMainThread(u64 userdata, s64 cyclesLate)
// Raise the event handler above on the CPU thread.
// s_token_finish_mutex must be locked.
// THIS IS EXECUTED FROM VIDEO THREAD
static void RaiseEvent()
static void RaiseEvent(int cycles_into_future)
{
if (s_event_raised)
return;

s_event_raised = true;

CoreTiming::FromThread from = CoreTiming::FromThread::NON_CPU;
if (!SConfig::GetInstance().bCPUThread || Fifo::UseDeterministicGPUThread())
s64 cycles = 0; // we don't care about timings for dual core mode.
if (!SConfig::GetInstance().bCPUThread || Fifo::UseDeterministicGPUThread()) {
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
// 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);

Expand All @@ -301,20 +307,20 @@ void SetToken(const u16 token, const bool interrupt)
s_token_pending = token;
s_token_interrupt_pending |= interrupt;

RaiseEvent();
RaiseEvent(cycles_into_future);
}

// SetFinish
// 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");

std::lock_guard<std::mutex> lk(s_token_finish_mutex);

s_finish_interrupt_pending |= true;

RaiseEvent();
RaiseEvent(cycles_into_future);
}

UPEAlphaReadReg GetAlphaReadMode()
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/VideoCommon/PixelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ void DoState(PointerWrap& p);
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

// gfx backend support
void SetToken(const u16 token, const bool interrupt);
void SetFinish();
void SetToken(const u16 token, const bool interrupt, int cycle_delay);
void SetFinish(int cycle_delay);
UPEAlphaReadReg GetAlphaReadMode();

} // namespace PixelEngine

0 comments on commit 263dd01

Please sign in to comment.