mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-19 13:05:45 -04:00
Implement event-based scheduler instead of lock-step components
This commit is contained in:
216
src/core/gpu.cpp
216
src/core/gpu.cpp
@ -1,6 +1,6 @@
|
||||
#include "gpu.h"
|
||||
#include "common/log.h"
|
||||
#include "common/heap_array.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "dma.h"
|
||||
#include "host_interface.h"
|
||||
@ -27,6 +27,8 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_timers = timers;
|
||||
m_force_progressive_scan = m_system->GetSettings().gpu_force_progressive_scan;
|
||||
m_tick_event =
|
||||
m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -63,6 +65,9 @@ void GPU::SoftReset()
|
||||
m_draw_mode.SetTextureWindow(0);
|
||||
UpdateGPUSTAT();
|
||||
UpdateCRTCConfig();
|
||||
|
||||
m_tick_event->Deactivate();
|
||||
UpdateSliceTicks();
|
||||
}
|
||||
|
||||
bool GPU::DoState(StateWrapper& sw)
|
||||
@ -273,6 +278,11 @@ void GPU::DMAWrite(const u32* words, u32 word_count)
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::Synchronize()
|
||||
{
|
||||
m_tick_event->InvokeEarly();
|
||||
}
|
||||
|
||||
void GPU::UpdateCRTCConfig()
|
||||
{
|
||||
static constexpr std::array<TickCount, 8> dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}};
|
||||
@ -357,31 +367,31 @@ void GPU::UpdateCRTCConfig()
|
||||
|
||||
// Ensure the numbers are sane, and not due to a misconfigured active display range.
|
||||
cs.display_aspect_ratio = (std::isnormal(display_ratio) && display_ratio != 0.0f) ? display_ratio : (4.0f / 3.0f);
|
||||
m_tick_event->SetInterval(cs.horizontal_total);
|
||||
}
|
||||
|
||||
UpdateSliceTicks();
|
||||
static TickCount GPUTicksToSystemTicks(u32 gpu_ticks)
|
||||
{
|
||||
// convert to master clock, rounding up as we want to overshoot not undershoot
|
||||
return (gpu_ticks * 7 + 10) / 11;
|
||||
}
|
||||
|
||||
void GPU::UpdateSliceTicks()
|
||||
{
|
||||
// the next event is at the end of the next scanline
|
||||
#if 1
|
||||
TickCount ticks_until_next_event;
|
||||
if (m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start)
|
||||
ticks_until_next_event = m_crtc_state.horizontal_display_start - m_crtc_state.current_tick_in_scanline;
|
||||
else if (m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_end)
|
||||
ticks_until_next_event = m_crtc_state.horizontal_display_end - m_crtc_state.current_tick_in_scanline;
|
||||
else
|
||||
ticks_until_next_event = m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline;
|
||||
#else
|
||||
// or at vblank. this will depend on the timer config..
|
||||
const TickCount ticks_until_next_event =
|
||||
((m_crtc_state.vertical_total - m_crtc_state.current_scanline) * m_crtc_state.horizontal_total) -
|
||||
m_crtc_state.current_tick_in_scanline;
|
||||
#endif
|
||||
// figure out how many GPU ticks until the next vblank
|
||||
const u32 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 =
|
||||
lines_until_vblank * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline;
|
||||
const u32 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);
|
||||
|
||||
// convert to master clock, rounding up as we want to overshoot not undershoot
|
||||
const TickCount system_ticks = (ticks_until_next_event * 7 + 10) / 11;
|
||||
m_system->SetDowncount(system_ticks);
|
||||
m_tick_event->Schedule(GPUTicksToSystemTicks(ticks_until_vblank));
|
||||
m_tick_event->SetPeriod(GPUTicksToSystemTicks(ticks_until_hblank));
|
||||
}
|
||||
|
||||
void GPU::Execute(TickCount ticks)
|
||||
@ -393,13 +403,76 @@ void GPU::Execute(TickCount ticks)
|
||||
m_crtc_state.fractional_ticks = temp % 7;
|
||||
}
|
||||
|
||||
while (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)
|
||||
{
|
||||
m_crtc_state.current_tick_in_scanline -= m_crtc_state.horizontal_total;
|
||||
m_crtc_state.current_scanline++;
|
||||
// short path when we execute <1 line.. this shouldn't occur often.
|
||||
const bool old_hblank = m_crtc_state.in_hblank;
|
||||
const bool new_hblank = m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start ||
|
||||
m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
|
||||
if (!old_hblank && new_hblank && m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
m_timers->AddTicks(HBLANK_TIMER_INDEX, 1);
|
||||
|
||||
UpdateSliceTicks();
|
||||
return;
|
||||
}
|
||||
|
||||
u32 lines_to_draw = m_crtc_state.current_tick_in_scanline / m_crtc_state.horizontal_total;
|
||||
m_crtc_state.current_tick_in_scanline %= m_crtc_state.horizontal_total;
|
||||
#if 0
|
||||
Log_WarningPrintf("Old line: %u, new line: %u, drawing %u", m_crtc_state.current_scanline,
|
||||
m_crtc_state.current_scanline + lines_to_draw, lines_to_draw);
|
||||
#endif
|
||||
|
||||
const bool old_hblank = m_crtc_state.in_hblank;
|
||||
const bool new_hblank = m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start ||
|
||||
m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
|
||||
m_crtc_state.in_hblank = new_hblank;
|
||||
if (m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
{
|
||||
const u32 hblank_timer_ticks = BoolToUInt32(!old_hblank) + BoolToUInt32(new_hblank) + (lines_to_draw - 1);
|
||||
m_timers->AddTicks(HBLANK_TIMER_INDEX, static_cast<TickCount>(hblank_timer_ticks));
|
||||
}
|
||||
|
||||
while (lines_to_draw > 0)
|
||||
{
|
||||
const u32 lines_to_draw_this_loop =
|
||||
std::min(lines_to_draw, m_crtc_state.vertical_total - m_crtc_state.current_scanline);
|
||||
const u32 prev_scanline = m_crtc_state.current_scanline;
|
||||
m_crtc_state.current_scanline += lines_to_draw_this_loop;
|
||||
DebugAssert(m_crtc_state.current_scanline <= m_crtc_state.vertical_total);
|
||||
lines_to_draw -= lines_to_draw_this_loop;
|
||||
|
||||
// clear the vblank flag if the beam would pass through the display area
|
||||
if (prev_scanline < m_crtc_state.vertical_display_start &&
|
||||
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end)
|
||||
{
|
||||
m_crtc_state.in_vblank = false;
|
||||
}
|
||||
|
||||
const bool new_vblank = m_crtc_state.current_scanline < m_crtc_state.vertical_display_start ||
|
||||
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end;
|
||||
if (m_crtc_state.in_vblank != new_vblank)
|
||||
{
|
||||
m_crtc_state.in_vblank = new_vblank;
|
||||
|
||||
if (new_vblank)
|
||||
{
|
||||
static u32 x = 0;
|
||||
Log_DebugPrintf("Now in v-blank %u ticks %u hblanks", m_system->GetGlobalTickCounter() - x);
|
||||
x = m_system->GetGlobalTickCounter();
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::VBLANK);
|
||||
|
||||
// flush any pending draws and "scan out" the image
|
||||
FlushRender();
|
||||
UpdateDisplay();
|
||||
m_system->IncrementFrameNumber();
|
||||
}
|
||||
|
||||
m_timers->SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||
}
|
||||
|
||||
// past the end of vblank?
|
||||
if (m_crtc_state.current_scanline >= m_crtc_state.vertical_total)
|
||||
if (m_crtc_state.current_scanline == m_crtc_state.vertical_total)
|
||||
{
|
||||
// start the new frame
|
||||
m_crtc_state.current_scanline = 0;
|
||||
@ -415,47 +488,18 @@ void GPU::Execute(TickCount ticks)
|
||||
m_GPUSTAT.interlaced_field = false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool new_vblank = m_crtc_state.current_scanline < m_crtc_state.vertical_display_start ||
|
||||
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end;
|
||||
if (m_crtc_state.in_vblank != new_vblank)
|
||||
{
|
||||
m_crtc_state.in_vblank = new_vblank;
|
||||
|
||||
if (new_vblank)
|
||||
{
|
||||
Log_DebugPrintf("Now in v-blank");
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::VBLANK);
|
||||
|
||||
// flush any pending draws and "scan out" the image
|
||||
FlushRender();
|
||||
UpdateDisplay();
|
||||
m_system->IncrementFrameNumber();
|
||||
}
|
||||
|
||||
m_timers->SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||
}
|
||||
|
||||
// alternating even line bit in 240-line mode
|
||||
if (m_GPUSTAT.In480iMode())
|
||||
{
|
||||
m_GPUSTAT.drawing_even_line =
|
||||
ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(!m_GPUSTAT.interlaced_field)) & u32(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_GPUSTAT.drawing_even_line =
|
||||
ConvertToBoolUnchecked((m_crtc_state.regs.Y + m_crtc_state.current_scanline) & u32(1));
|
||||
}
|
||||
}
|
||||
|
||||
const bool new_hblank = m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start ||
|
||||
m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
|
||||
if (m_crtc_state.in_hblank != new_hblank)
|
||||
// alternating even line bit in 240-line mode
|
||||
if (m_GPUSTAT.In480iMode())
|
||||
{
|
||||
m_crtc_state.in_hblank = new_hblank;
|
||||
if (new_hblank && m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
m_timers->AddTicks(HBLANK_TIMER_INDEX, 1);
|
||||
m_GPUSTAT.drawing_even_line =
|
||||
ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(!m_GPUSTAT.interlaced_field)) & u32(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_GPUSTAT.drawing_even_line =
|
||||
ConvertToBoolUnchecked((m_crtc_state.regs.Y + m_crtc_state.current_scanline) & u32(1));
|
||||
}
|
||||
|
||||
UpdateSliceTicks();
|
||||
@ -560,17 +604,29 @@ void GPU::WriteGP1(u32 value)
|
||||
|
||||
case 0x06: // Set horizontal display range
|
||||
{
|
||||
m_crtc_state.regs.horizontal_display_range = param & CRTCState::Regs::HORIZONTAL_DISPLAY_RANGE_MASK;
|
||||
Log_DebugPrintf("Horizontal display range <- 0x%08X", m_crtc_state.regs.horizontal_display_range);
|
||||
UpdateCRTCConfig();
|
||||
const u32 new_value = param & CRTCState::Regs::HORIZONTAL_DISPLAY_RANGE_MASK;
|
||||
Log_DebugPrintf("Horizontal display range <- 0x%08X", new_value);
|
||||
|
||||
if (m_crtc_state.regs.horizontal_display_range != new_value)
|
||||
{
|
||||
m_tick_event->InvokeEarly(true);
|
||||
m_crtc_state.regs.horizontal_display_range = new_value;
|
||||
UpdateCRTCConfig();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07: // Set display start address
|
||||
{
|
||||
m_crtc_state.regs.vertical_display_range = param & CRTCState::Regs::VERTICAL_DISPLAY_RANGE_MASK;
|
||||
Log_DebugPrintf("Vertical display range <- 0x%08X", m_crtc_state.regs.vertical_display_range);
|
||||
UpdateCRTCConfig();
|
||||
const u32 new_value = param & CRTCState::Regs::VERTICAL_DISPLAY_RANGE_MASK;
|
||||
Log_DebugPrintf("Vertical display range <- 0x%08X", new_value);
|
||||
|
||||
if (m_crtc_state.regs.vertical_display_range != new_value)
|
||||
{
|
||||
m_tick_event->InvokeEarly(true);
|
||||
m_crtc_state.regs.vertical_display_range = new_value;
|
||||
UpdateCRTCConfig();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -590,16 +646,22 @@ void GPU::WriteGP1(u32 value)
|
||||
};
|
||||
|
||||
const GP1_08h dm{param};
|
||||
m_GPUSTAT.horizontal_resolution_1 = dm.horizontal_resolution_1;
|
||||
m_GPUSTAT.vertical_resolution = dm.vertical_resolution;
|
||||
m_GPUSTAT.pal_mode = dm.pal_mode;
|
||||
m_GPUSTAT.display_area_color_depth_24 = dm.display_area_color_depth;
|
||||
m_GPUSTAT.vertical_interlace = dm.vertical_interlace;
|
||||
m_GPUSTAT.horizontal_resolution_2 = dm.horizontal_resolution_2;
|
||||
m_GPUSTAT.reverse_flag = dm.reverse_flag;
|
||||
|
||||
GPUSTAT new_GPUSTAT{m_GPUSTAT.bits};
|
||||
new_GPUSTAT.horizontal_resolution_1 = dm.horizontal_resolution_1;
|
||||
new_GPUSTAT.vertical_resolution = dm.vertical_resolution;
|
||||
new_GPUSTAT.pal_mode = dm.pal_mode;
|
||||
new_GPUSTAT.display_area_color_depth_24 = dm.display_area_color_depth;
|
||||
new_GPUSTAT.vertical_interlace = dm.vertical_interlace;
|
||||
new_GPUSTAT.horizontal_resolution_2 = dm.horizontal_resolution_2;
|
||||
new_GPUSTAT.reverse_flag = dm.reverse_flag;
|
||||
Log_DebugPrintf("Set display mode <- 0x%08X", dm.bits);
|
||||
UpdateCRTCConfig();
|
||||
|
||||
if (m_GPUSTAT.bits != new_GPUSTAT.bits)
|
||||
{
|
||||
m_tick_event->InvokeEarly(true);
|
||||
m_GPUSTAT.bits = new_GPUSTAT.bits;
|
||||
UpdateCRTCConfig();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
Reference in New Issue
Block a user