GPU: Timing for CPU->VRAM transfers

Fixes Monkey Hero, probably others.
This commit is contained in:
Connor McLaughlin 2020-03-01 17:06:38 +10:00
parent 6b9c6d3750
commit b0b1fd8f1a
5 changed files with 56 additions and 26 deletions

View file

@ -65,7 +65,7 @@ void GPU::SoftReset()
SetDrawMode(0); SetDrawMode(0);
SetTexturePalette(0); SetTexturePalette(0);
m_draw_mode.SetTextureWindow(0); m_draw_mode.SetTextureWindow(0);
UpdateGPUSTAT(); UpdateDMARequest();
UpdateCRTCConfig(); UpdateCRTCConfig();
m_tick_event->Deactivate(); m_tick_event->Deactivate();
@ -129,6 +129,9 @@ bool GPU::DoState(StateWrapper& sw)
sw.Do(&m_crtc_state.in_hblank); sw.Do(&m_crtc_state.in_hblank);
sw.Do(&m_crtc_state.in_vblank); sw.Do(&m_crtc_state.in_vblank);
sw.Do(&m_state);
sw.Do(&m_blitter_ticks);
sw.Do(&m_command_total_words);
sw.Do(&m_GPUREAD_latch); sw.Do(&m_GPUREAD_latch);
sw.Do(&m_vram_transfer.x); sw.Do(&m_vram_transfer.x);
@ -146,7 +149,7 @@ bool GPU::DoState(StateWrapper& sw)
m_draw_mode.texture_window_changed = true; m_draw_mode.texture_window_changed = true;
m_drawing_area_changed = true; m_drawing_area_changed = true;
m_drawing_offset_changed = true; m_drawing_offset_changed = true;
UpdateGPUSTAT(); UpdateDMARequest();
} }
if (!sw.DoMarker("GPU-VRAM")) if (!sw.DoMarker("GPU-VRAM"))
@ -184,12 +187,18 @@ void GPU::ResetGraphicsAPIState() {}
void GPU::RestoreGraphicsAPIState() {} void GPU::RestoreGraphicsAPIState() {}
void GPU::UpdateGPUSTAT() void GPU::UpdateDMARequest()
{ {
m_GPUSTAT.ready_to_send_vram = (m_state == State::ReadingVRAM); // we can kill the blitter ticks here if enough time has passed
m_GPUSTAT.ready_to_recieve_cmd = (m_state == State::Idle); if (m_blitter_ticks > 0 && GetPendingGPUTicks() >= m_blitter_ticks)
m_blitter_ticks = 0;
const bool blitter_idle = (m_blitter_ticks <= 0);
m_GPUSTAT.ready_to_send_vram = (blitter_idle && m_state == State::ReadingVRAM);
m_GPUSTAT.ready_to_recieve_cmd = (blitter_idle && m_state == State::Idle);
m_GPUSTAT.ready_to_recieve_dma = m_GPUSTAT.ready_to_recieve_dma =
(m_state == State::Idle || (m_state != State::ReadingVRAM && m_command_total_words > 0)); blitter_idle && (m_state == State::Idle || (m_state != State::ReadingVRAM && m_command_total_words > 0));
bool dma_request; bool dma_request;
switch (m_GPUSTAT.dma_direction) switch (m_GPUSTAT.dma_direction)
@ -199,15 +208,15 @@ void GPU::UpdateGPUSTAT()
break; break;
case DMADirection::FIFO: case DMADirection::FIFO:
dma_request = true; // FIFO not full/full dma_request = blitter_idle && m_state >= State::ReadingVRAM; // FIFO not full/full
break; break;
case DMADirection::CPUtoGP0: case DMADirection::CPUtoGP0:
dma_request = m_GPUSTAT.ready_to_recieve_dma; dma_request = blitter_idle && m_GPUSTAT.ready_to_recieve_dma;
break; break;
case DMADirection::GPUREADtoCPU: case DMADirection::GPUREADtoCPU:
dma_request = m_GPUSTAT.ready_to_send_vram; dma_request = blitter_idle && m_GPUSTAT.ready_to_send_vram;
break; break;
default: default:
@ -273,6 +282,13 @@ void GPU::DMAWrite(const u32* words, u32 word_count)
{ {
std::copy(words, words + word_count, std::back_inserter(m_GP0_buffer)); std::copy(words, words + word_count, std::back_inserter(m_GP0_buffer));
ExecuteCommands(); ExecuteCommands();
if (m_state == State::WritingVRAM)
{
m_blitter_ticks += word_count;
UpdateDMARequest();
UpdateSliceTicks();
}
} }
break; break;
@ -423,18 +439,19 @@ TickCount GPU::GetPendingGPUTicks() const
void GPU::UpdateSliceTicks() void GPU::UpdateSliceTicks()
{ {
// figure out how many GPU ticks until the next vblank // figure out how many GPU ticks until the next vblank
const u32 lines_until_vblank = const TickCount lines_until_vblank =
(m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end ? (m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end ?
(m_crtc_state.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) : (m_crtc_state.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) :
(m_crtc_state.vertical_display_end - m_crtc_state.current_scanline)); (m_crtc_state.vertical_display_end - m_crtc_state.current_scanline));
const u32 ticks_until_vblank = const TickCount ticks_until_vblank =
lines_until_vblank * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline; lines_until_vblank * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline;
const u32 ticks_until_hblank = const TickCount ticks_until_hblank =
(m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end) ? (m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end) ?
(m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline + m_crtc_state.horizontal_display_end) : (m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline + m_crtc_state.horizontal_display_end) :
(m_crtc_state.horizontal_display_end - m_crtc_state.current_tick_in_scanline); (m_crtc_state.horizontal_display_end - m_crtc_state.current_tick_in_scanline);
m_tick_event->Schedule(GPUTicksToSystemTicks(ticks_until_vblank)); m_tick_event->Schedule(
GPUTicksToSystemTicks((m_blitter_ticks > 0) ? std::min(m_blitter_ticks, ticks_until_vblank) : ticks_until_vblank));
m_tick_event->SetPeriod(GPUTicksToSystemTicks(ticks_until_hblank)); m_tick_event->SetPeriod(GPUTicksToSystemTicks(ticks_until_hblank));
} }
@ -442,9 +459,20 @@ void GPU::Execute(TickCount ticks)
{ {
// convert cpu/master clock to GPU ticks, accounting for partial cycles because of the non-integer divider // convert cpu/master clock to GPU ticks, accounting for partial cycles because of the non-integer divider
{ {
const TickCount temp = (ticks * 11) + m_crtc_state.fractional_ticks; const TickCount ticks_mul_11 = (ticks * 11) + m_crtc_state.fractional_ticks;
m_crtc_state.current_tick_in_scanline += temp / 7; const TickCount gpu_ticks = ticks_mul_11 / 7;
m_crtc_state.fractional_ticks = temp % 7; m_crtc_state.fractional_ticks = ticks_mul_11 % 7;
m_crtc_state.current_tick_in_scanline += gpu_ticks;
if (m_blitter_ticks > 0)
{
m_blitter_ticks -= gpu_ticks;
if (m_blitter_ticks <= 0)
{
m_blitter_ticks = 0;
UpdateDMARequest();
}
}
} }
if (m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_total) if (m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_total)
@ -570,7 +598,7 @@ u32 GPU::ReadGPUREAD()
Log_DebugPrintf("End of VRAM->CPU transfer"); Log_DebugPrintf("End of VRAM->CPU transfer");
m_vram_transfer = {}; m_vram_transfer = {};
m_state = State::Idle; m_state = State::Idle;
UpdateGPUSTAT(); UpdateDMARequest();
// end of transfer, catch up on any commands which were written (unlikely) // end of transfer, catch up on any commands which were written (unlikely)
ExecuteCommands(); ExecuteCommands();
@ -609,7 +637,7 @@ void GPU::WriteGP1(u32 value)
m_command_total_words = 0; m_command_total_words = 0;
m_vram_transfer = {}; m_vram_transfer = {};
m_GP0_buffer.clear(); m_GP0_buffer.clear();
UpdateGPUSTAT(); UpdateDMARequest();
} }
break; break;
@ -633,7 +661,7 @@ void GPU::WriteGP1(u32 value)
{ {
m_GPUSTAT.dma_direction = static_cast<DMADirection>(param); m_GPUSTAT.dma_direction = static_cast<DMADirection>(param);
Log_DebugPrintf("DMA direction <- 0x%02X", static_cast<u32>(m_GPUSTAT.dma_direction.GetValue())); Log_DebugPrintf("DMA direction <- 0x%02X", static_cast<u32>(m_GPUSTAT.dma_direction.GetValue()));
UpdateGPUSTAT(); UpdateDMARequest();
} }
break; break;

View file

@ -302,7 +302,7 @@ protected:
void UpdateSliceTicks(); void UpdateSliceTicks();
// Updates dynamic bits in GPUSTAT (ready to send VRAM/ready to receive DMA) // Updates dynamic bits in GPUSTAT (ready to send VRAM/ready to receive DMA)
void UpdateGPUSTAT(); void UpdateDMARequest();
// Ticks for hblank/vblank. // Ticks for hblank/vblank.
void Execute(TickCount ticks); void Execute(TickCount ticks);
@ -560,7 +560,12 @@ protected:
} m_crtc_state = {}; } m_crtc_state = {};
State m_state = State::Idle; State m_state = State::Idle;
TickCount m_blitter_ticks = 0;
u32 m_command_total_words = 0; u32 m_command_total_words = 0;
/// GPUREAD value for non-VRAM-reads.
u32 m_GPUREAD_latch = 0;
struct VRAMTransfer struct VRAMTransfer
{ {
u16 x; u16 x;
@ -571,9 +576,6 @@ protected:
u16 row; u16 row;
} m_vram_transfer = {}; } m_vram_transfer = {};
/// GPUREAD value for non-VRAM-reads.
u32 m_GPUREAD_latch = 0;
std::vector<u32> m_GP0_buffer; std::vector<u32> m_GP0_buffer;
struct Stats struct Stats

View file

@ -45,7 +45,7 @@ void GPU::ExecuteCommands()
else if (command_ptr > m_GP0_buffer.data()) else if (command_ptr > m_GP0_buffer.data())
m_GP0_buffer.erase(m_GP0_buffer.begin(), m_GP0_buffer.begin() + (command_ptr - m_GP0_buffer.data())); m_GP0_buffer.erase(m_GP0_buffer.begin(), m_GP0_buffer.begin() + (command_ptr - m_GP0_buffer.data()));
UpdateGPUSTAT(); UpdateDMARequest();
} }
void GPU::EndCommand() void GPU::EndCommand()

View file

@ -34,7 +34,7 @@ private:
static constexpr u32 DATA_IN_FIFO_SIZE = 256 * 4; static constexpr u32 DATA_IN_FIFO_SIZE = 256 * 4;
static constexpr u32 DATA_OUT_FIFO_SIZE = 192 * 4; static constexpr u32 DATA_OUT_FIFO_SIZE = 192 * 4;
static constexpr u32 NUM_BLOCKS = 6; static constexpr u32 NUM_BLOCKS = 6;
static constexpr TickCount TICKS_PER_BLOCK = 256; static constexpr TickCount TICKS_PER_BLOCK = 3072;
enum DataOutputDepth : u8 enum DataOutputDepth : u8
{ {

View file

@ -2,4 +2,4 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 2; static constexpr u32 SAVE_STATE_VERSION = 3;