diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 1ece9f278ee..9518fd04468 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -13,7 +13,6 @@ #include "Common/EnumMap.h" #include "Common/Logging/Log.h" -#include "Core/CoreTiming.h" #include "Core/DolphinAnalytics.h" #include "Core/FifoPlayer/FifoPlayer.h" #include "Core/FifoPlayer/FifoRecorder.h" @@ -359,14 +358,8 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager& if (g_ActiveConfig.bImmediateXFB) { - // TODO: GetTicks is not sane from the GPU thread. - // This value is currently used for frame dumping and the custom shader "time_ms" value. - // Frame dumping has more calls that aren't sane from the GPU thread. - // i.e. Frame dumping is not sane in "Dual Core" mode in general. - const u64 ticks = system.GetCoreTiming().GetTicks(); - // below div two to convert from bytes to pixels - it expects width, not stride - g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height, ticks); + g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height); } else { diff --git a/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp index e0e61529f73..52d41549dd8 100644 --- a/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp +++ b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp @@ -65,6 +65,7 @@ namespace { AVRational GetTimeBaseForCurrentRefreshRate(s64 max_denominator) { + // TODO: GetTargetRefreshRate* are not safe from GPU thread. auto& vi = Core::System::GetInstance().GetVideoInterface(); int num; int den; @@ -368,6 +369,7 @@ void FFMpegFrameDump::AddFrame(const FrameData& frame) // Calculate presentation timestamp from ticks since start. const s64 pts = av_rescale_q( frame.state.ticks - m_context->start_ticks, + // TODO: GetTicksPerSecond is not safe from GPU thread. AVRational{1, int(Core::System::GetInstance().GetSystemTimers().GetTicksPerSecond())}, m_context->codec->time_base); diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index ce4b62a85b7..bedc1c128f6 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -215,12 +215,14 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, } } -void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) +void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height) { + const u64 ticks = m_next_swap_estimated_ticks; + FetchXFB(xfb_addr, fb_width, fb_stride, fb_height, ticks); PresentInfo present_info; - present_info.emulated_timestamp = ticks; // TODO: This should be the time of the next VI field + present_info.emulated_timestamp = ticks; present_info.frame_count = m_frame_count++; present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; @@ -235,6 +237,12 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ video_events.after_present_event.Trigger(present_info); } +void Presenter::SetNextSwapEstimatedTime(u64 ticks, TimePoint host_time) +{ + m_next_swap_estimated_ticks = ticks; + m_next_swap_estimated_time = host_time; +} + void Presenter::ProcessFrameDumping(u64 ticks) const { if (g_frame_dumper->IsFrameDumping() && m_xfb_entry) @@ -938,8 +946,10 @@ void Presenter::DoState(PointerWrap& p) // This technically counts as the end of the frame GetVideoEvents().after_frame_event.Trigger(Core::System::GetInstance()); - ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height, - m_last_xfb_ticks); + m_next_swap_estimated_ticks = m_last_xfb_ticks; + m_next_swap_estimated_time = Clock::now(); + + ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height); } } diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index a355af43856..d6662f97f13 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -37,7 +37,9 @@ public: void ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks, TimePoint presentation_time); - void ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); + void ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height); + + void SetNextSwapEstimatedTime(u64 ticks, TimePoint host_time); void Present(std::optional presentation_time = std::nullopt); void ClearLastXfbId() { m_last_xfb_id = std::numeric_limits::max(); } @@ -167,6 +169,11 @@ private: u32 m_last_xfb_height = MAX_XFB_HEIGHT; Common::EventHook m_config_changed; + + // Calculated from the previous swap time and current refresh rate. + // Can be used for presentation of ImmediateXFB swaps which don't have timing information. + u64 m_next_swap_estimated_ticks = 0; + TimePoint m_next_swap_estimated_time{Clock::now()}; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 676349b65bb..23d9d6e7f42 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -21,6 +21,8 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/DolphinAnalytics.h" +#include "Core/HW/SystemTimers.h" +#include "Core/HW/VideoInterface.h" #include "Core/System.h" // TODO: ugly @@ -93,16 +95,35 @@ std::string VideoBackendBase::BadShaderFilename(const char* shader_stage, int co void VideoBackendBase::Video_OutputXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - if (m_initialized && g_presenter && !g_ActiveConfig.bImmediateXFB) + if (!m_initialized || !g_presenter) + return; + + auto& system = Core::System::GetInstance(); + auto& core_timing = system.GetCoreTiming(); + + if (!g_ActiveConfig.bImmediateXFB) { - auto& system = Core::System::GetInstance(); system.GetFifo().SyncGPU(Fifo::SyncGPUReason::Swap); - const TimePoint presentation_time = system.GetCoreTiming().GetTargetHostTime(ticks); + const TimePoint presentation_time = core_timing.GetTargetHostTime(ticks); AsyncRequests::GetInstance()->PushEvent([=] { g_presenter->ViSwap(xfb_addr, fb_width, fb_stride, fb_height, ticks, presentation_time); }); } + + // Inform the Presenter of the next estimated swap time. + + auto& vi = system.GetVideoInterface(); + const s64 refresh_rate_den = vi.GetTargetRefreshRateDenominator(); + const s64 refresh_rate_num = vi.GetTargetRefreshRateNumerator(); + + const auto next_swap_estimated_ticks = + ticks + (system.GetSystemTimers().GetTicksPerSecond() * refresh_rate_den / refresh_rate_num); + const auto next_swap_estimated_time = core_timing.GetTargetHostTime(next_swap_estimated_ticks); + + AsyncRequests::GetInstance()->PushEvent([=] { + g_presenter->SetNextSwapEstimatedTime(next_swap_estimated_ticks, next_swap_estimated_time); + }); } u32 VideoBackendBase::Video_GetQueryResult(PerfQueryType type) diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h index ccee5bc3bda..b7ecf0fca58 100644 --- a/Source/Core/VideoCommon/VideoEvents.h +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -34,7 +34,6 @@ struct PresentInfo PresentReason reason = PresentReason::Immediate; // The exact emulated time of the when real hardware would have presented this frame - // FIXME: Immediate should predict the timestamp of this present u64 emulated_timestamp = 0; // TODO: