Merge pull request #9275 from JosJuice/framedump-boot-time

FrameDump: Start timing at 0 ticks when starting from boot
This commit is contained in:
Léo Lam 2020-12-05 23:57:41 +01:00 committed by GitHub
commit d8b9a040ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 24 deletions

View File

@ -47,7 +47,7 @@ struct FrameDumpContext
int width = 0; int width = 0;
int height = 0; int height = 0;
u64 first_frame_ticks = 0; u64 start_ticks = 0;
u32 savestate_index = 0; u32 savestate_index = 0;
bool gave_vfr_warning = false; bool gave_vfr_warning = false;
@ -111,7 +111,7 @@ std::string GetDumpPath(const std::string& extension, std::time_t time, u32 inde
} // namespace } // namespace
bool FrameDump::Start(int w, int h) bool FrameDump::Start(int w, int h, u64 start_ticks)
{ {
if (IsStarted()) if (IsStarted())
return true; return true;
@ -120,16 +120,19 @@ bool FrameDump::Start(int w, int h)
m_start_time = std::time(nullptr); m_start_time = std::time(nullptr);
m_file_index = 0; m_file_index = 0;
return PrepareEncoding(w, h); return PrepareEncoding(w, h, start_ticks, m_savestate_index);
} }
bool FrameDump::PrepareEncoding(int w, int h) bool FrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_index)
{ {
m_context = std::make_unique<FrameDumpContext>(); m_context = std::make_unique<FrameDumpContext>();
m_context->width = w; m_context->width = w;
m_context->height = h; m_context->height = h;
m_context->start_ticks = start_ticks;
m_context->savestate_index = savestate_index;
InitAVCodec(); InitAVCodec();
const bool success = CreateVideoFile(); const bool success = CreateVideoFile();
if (!success) if (!success)
@ -279,14 +282,8 @@ void FrameDump::AddFrame(const FrameData& frame)
if (!IsStarted()) if (!IsStarted())
return; return;
if (IsFirstFrameInCurrentFile()) // Calculate presentation timestamp from ticks since start.
{ const s64 pts = av_rescale_q(frame.state.ticks - m_context->start_ticks,
m_context->first_frame_ticks = frame.state.ticks;
m_context->savestate_index = frame.state.savestate_index;
}
// Calculate presentation timestamp from current ticks since first frame ticks.
const s64 pts = av_rescale_q(frame.state.ticks - m_context->first_frame_ticks,
AVRational{1, int(SystemTimers::GetTicksPerSecond())}, AVRational{1, int(SystemTimers::GetTicksPerSecond())},
m_context->codec->time_base); m_context->codec->time_base);
@ -300,7 +297,7 @@ void FrameDump::AddFrame(const FrameData& frame)
else if (pts > m_context->last_pts + 1 && !m_context->gave_vfr_warning) else if (pts > m_context->last_pts + 1 && !m_context->gave_vfr_warning)
{ {
WARN_LOG_FMT(FRAMEDUMP, "PTS delta > 1. Resulting file will have variable frame rate. " WARN_LOG_FMT(FRAMEDUMP, "PTS delta > 1. Resulting file will have variable frame rate. "
"Subsequent occurances will not be reported."); "Subsequent occurrences will not be reported.");
m_context->gave_vfr_warning = true; m_context->gave_vfr_warning = true;
} }
} }
@ -447,14 +444,15 @@ void FrameDump::CheckForConfigChange(const FrameData& frame)
{ {
Stop(); Stop();
++m_file_index; ++m_file_index;
PrepareEncoding(frame.width, frame.height); PrepareEncoding(frame.width, frame.height, frame.state.ticks, frame.state.savestate_index);
} }
} }
FrameDump::FrameState FrameDump::FetchState(u64 ticks) const FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const
{ {
FrameState state; FrameState state;
state.ticks = ticks; state.ticks = ticks;
state.frame_number = frame_number;
state.savestate_index = m_savestate_index; state.savestate_index = m_savestate_index;
const auto time_base = GetTimeBaseForCurrentRefreshRate(); const auto time_base = GetTimeBaseForCurrentRefreshRate();

View File

@ -23,6 +23,7 @@ public:
struct FrameState struct FrameState
{ {
u64 ticks = 0; u64 ticks = 0;
int frame_number = 0;
u32 savestate_index = 0; u32 savestate_index = 0;
int refresh_rate_num = 0; int refresh_rate_num = 0;
int refresh_rate_den = 0; int refresh_rate_den = 0;
@ -37,16 +38,16 @@ public:
FrameState state; FrameState state;
}; };
bool Start(int w, int h); bool Start(int w, int h, u64 start_ticks);
void AddFrame(const FrameData&); void AddFrame(const FrameData&);
void Stop(); void Stop();
void DoState(PointerWrap&); void DoState(PointerWrap&);
bool IsStarted() const; bool IsStarted() const;
FrameState FetchState(u64 ticks) const; FrameState FetchState(u64 ticks, int frame_number) const;
private: private:
bool IsFirstFrameInCurrentFile() const; bool IsFirstFrameInCurrentFile() const;
bool PrepareEncoding(int w, int h); bool PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_index);
bool CreateVideoFile(); bool CreateVideoFile();
void CloseVideoFile(); void CloseVideoFile();
void CheckForConfigChange(const FrameData&); void CheckForConfigChange(const FrameData&);
@ -68,7 +69,7 @@ private:
inline FrameDump::FrameDump() = default; inline FrameDump::FrameDump() = default;
inline FrameDump::~FrameDump() = default; inline FrameDump::~FrameDump() = default;
inline FrameDump::FrameState FrameDump::FetchState(u64 ticks) const inline FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const
{ {
return {}; return {};
} }

View File

@ -1290,7 +1290,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample));
if (IsFrameDumping()) if (IsFrameDumping())
DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks); DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks, m_frame_count);
// Begin new frame // Begin new frame
m_frame_count++; m_frame_count++;
@ -1380,7 +1380,8 @@ bool Renderer::IsFrameDumping() const
} }
void Renderer::DumpCurrentFrame(const AbstractTexture* src_texture, void Renderer::DumpCurrentFrame(const AbstractTexture* src_texture,
const MathUtil::Rectangle<int>& src_rect, u64 ticks) const MathUtil::Rectangle<int>& src_rect, u64 ticks,
int frame_number)
{ {
int source_width = src_rect.GetWidth(); int source_width = src_rect.GetWidth();
int source_height = src_rect.GetHeight(); int source_height = src_rect.GetHeight();
@ -1414,7 +1415,7 @@ void Renderer::DumpCurrentFrame(const AbstractTexture* src_texture,
m_frame_dump_readback_texture->CopyFromTexture(src_texture, copy_rect, 0, 0, m_frame_dump_readback_texture->CopyFromTexture(src_texture, copy_rect, 0, 0,
m_frame_dump_readback_texture->GetRect()); m_frame_dump_readback_texture->GetRect());
m_last_frame_state = m_frame_dump.FetchState(ticks); m_last_frame_state = m_frame_dump.FetchState(ticks, frame_number);
m_frame_dump_needs_flush = true; m_frame_dump_needs_flush = true;
} }
@ -1619,7 +1620,11 @@ void Renderer::FrameDumpThreadFunc()
bool Renderer::StartFrameDumpToFFMPEG(const FrameDump::FrameData& frame) bool Renderer::StartFrameDumpToFFMPEG(const FrameDump::FrameData& frame)
{ {
return m_frame_dump.Start(frame.width, frame.height); // If dumping started at boot, the start time must be set to the boot time to maintain audio sync.
// TODO: Perhaps we should care about this when starting dumping in the middle of emulation too,
// but it's less important there since the first frame to dump usually gets delivered quickly.
const u64 start_ticks = frame.state.frame_number == 0 ? 0 : frame.state.ticks;
return m_frame_dump.Start(frame.width, frame.height, start_ticks);
} }
void Renderer::DumpFrameToFFMPEG(const FrameDump::FrameData& frame) void Renderer::DumpFrameToFFMPEG(const FrameDump::FrameData& frame)

View File

@ -411,7 +411,7 @@ private:
// Fills the frame dump staging texture with the current XFB texture. // Fills the frame dump staging texture with the current XFB texture.
void DumpCurrentFrame(const AbstractTexture* src_texture, void DumpCurrentFrame(const AbstractTexture* src_texture,
const MathUtil::Rectangle<int>& src_rect, u64 ticks); const MathUtil::Rectangle<int>& src_rect, u64 ticks, int frame_number);
// Asynchronously encodes the specified pointer of frame data to the frame dump. // Asynchronously encodes the specified pointer of frame data to the frame dump.
void DumpFrameData(const u8* data, int w, int h, int stride); void DumpFrameData(const u8* data, int w, int h, int stride);