mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-17 22:25:37 +00:00
GPU: Timing for CPU->VRAM transfers
Fixes Monkey Hero, probably others.
This commit is contained in:
parent
6b9c6d3750
commit
b0b1fd8f1a
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue