From b0b1fd8f1ae93f2d5c034839915db7789953433a Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 1 Mar 2020 17:06:38 +1000 Subject: [PATCH] GPU: Timing for CPU->VRAM transfers Fixes Monkey Hero, probably others. --- src/core/gpu.cpp | 66 +++++++++++++++++++++++++---------- src/core/gpu.h | 10 +++--- src/core/gpu_commands.cpp | 2 +- src/core/mdec.h | 2 +- src/core/save_state_version.h | 2 +- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 8dd91d7d0..55aa9c8a2 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -65,7 +65,7 @@ void GPU::SoftReset() SetDrawMode(0); SetTexturePalette(0); m_draw_mode.SetTextureWindow(0); - UpdateGPUSTAT(); + UpdateDMARequest(); UpdateCRTCConfig(); 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_vblank); + sw.Do(&m_state); + sw.Do(&m_blitter_ticks); + sw.Do(&m_command_total_words); sw.Do(&m_GPUREAD_latch); sw.Do(&m_vram_transfer.x); @@ -146,7 +149,7 @@ bool GPU::DoState(StateWrapper& sw) m_draw_mode.texture_window_changed = true; m_drawing_area_changed = true; m_drawing_offset_changed = true; - UpdateGPUSTAT(); + UpdateDMARequest(); } if (!sw.DoMarker("GPU-VRAM")) @@ -184,12 +187,18 @@ void GPU::ResetGraphicsAPIState() {} void GPU::RestoreGraphicsAPIState() {} -void GPU::UpdateGPUSTAT() +void GPU::UpdateDMARequest() { - m_GPUSTAT.ready_to_send_vram = (m_state == State::ReadingVRAM); - m_GPUSTAT.ready_to_recieve_cmd = (m_state == State::Idle); + // we can kill the blitter ticks here if enough time has passed + 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_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; switch (m_GPUSTAT.dma_direction) @@ -199,15 +208,15 @@ void GPU::UpdateGPUSTAT() break; case DMADirection::FIFO: - dma_request = true; // FIFO not full/full + dma_request = blitter_idle && m_state >= State::ReadingVRAM; // FIFO not full/full break; case DMADirection::CPUtoGP0: - dma_request = m_GPUSTAT.ready_to_recieve_dma; + dma_request = blitter_idle && m_GPUSTAT.ready_to_recieve_dma; break; case DMADirection::GPUREADtoCPU: - dma_request = m_GPUSTAT.ready_to_send_vram; + dma_request = blitter_idle && m_GPUSTAT.ready_to_send_vram; break; 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)); ExecuteCommands(); + + if (m_state == State::WritingVRAM) + { + m_blitter_ticks += word_count; + UpdateDMARequest(); + UpdateSliceTicks(); + } } break; @@ -423,18 +439,19 @@ TickCount GPU::GetPendingGPUTicks() const void GPU::UpdateSliceTicks() { // 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.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) : (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; - 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.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_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)); } @@ -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 { - const TickCount temp = (ticks * 11) + m_crtc_state.fractional_ticks; - m_crtc_state.current_tick_in_scanline += temp / 7; - m_crtc_state.fractional_ticks = temp % 7; + const TickCount ticks_mul_11 = (ticks * 11) + m_crtc_state.fractional_ticks; + const TickCount gpu_ticks = ticks_mul_11 / 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) @@ -570,7 +598,7 @@ u32 GPU::ReadGPUREAD() Log_DebugPrintf("End of VRAM->CPU transfer"); m_vram_transfer = {}; m_state = State::Idle; - UpdateGPUSTAT(); + UpdateDMARequest(); // end of transfer, catch up on any commands which were written (unlikely) ExecuteCommands(); @@ -609,7 +637,7 @@ void GPU::WriteGP1(u32 value) m_command_total_words = 0; m_vram_transfer = {}; m_GP0_buffer.clear(); - UpdateGPUSTAT(); + UpdateDMARequest(); } break; @@ -633,7 +661,7 @@ void GPU::WriteGP1(u32 value) { m_GPUSTAT.dma_direction = static_cast(param); Log_DebugPrintf("DMA direction <- 0x%02X", static_cast(m_GPUSTAT.dma_direction.GetValue())); - UpdateGPUSTAT(); + UpdateDMARequest(); } break; diff --git a/src/core/gpu.h b/src/core/gpu.h index 4761bfde4..d2522c2bf 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -302,7 +302,7 @@ protected: void UpdateSliceTicks(); // Updates dynamic bits in GPUSTAT (ready to send VRAM/ready to receive DMA) - void UpdateGPUSTAT(); + void UpdateDMARequest(); // Ticks for hblank/vblank. void Execute(TickCount ticks); @@ -560,7 +560,12 @@ protected: } m_crtc_state = {}; State m_state = State::Idle; + TickCount m_blitter_ticks = 0; u32 m_command_total_words = 0; + + /// GPUREAD value for non-VRAM-reads. + u32 m_GPUREAD_latch = 0; + struct VRAMTransfer { u16 x; @@ -571,9 +576,6 @@ protected: u16 row; } m_vram_transfer = {}; - /// GPUREAD value for non-VRAM-reads. - u32 m_GPUREAD_latch = 0; - std::vector m_GP0_buffer; struct Stats diff --git a/src/core/gpu_commands.cpp b/src/core/gpu_commands.cpp index 2a824ab24..599667403 100644 --- a/src/core/gpu_commands.cpp +++ b/src/core/gpu_commands.cpp @@ -45,7 +45,7 @@ void GPU::ExecuteCommands() 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())); - UpdateGPUSTAT(); + UpdateDMARequest(); } void GPU::EndCommand() diff --git a/src/core/mdec.h b/src/core/mdec.h index d1e4b4a9a..4b98d3996 100644 --- a/src/core/mdec.h +++ b/src/core/mdec.h @@ -34,7 +34,7 @@ private: static constexpr u32 DATA_IN_FIFO_SIZE = 256 * 4; static constexpr u32 DATA_OUT_FIFO_SIZE = 192 * 4; static constexpr u32 NUM_BLOCKS = 6; - static constexpr TickCount TICKS_PER_BLOCK = 256; + static constexpr TickCount TICKS_PER_BLOCK = 3072; enum DataOutputDepth : u8 { diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index b48c14f9d..b73137f3e 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,4 +2,4 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 2; +static constexpr u32 SAVE_STATE_VERSION = 3;