Merge pull request #9275 from JosJuice/framedump-boot-time
FrameDump: Start timing at 0 ticks when starting from boot
This commit is contained in:
commit
d8b9a040ed
|
@ -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();
|
||||||
|
|
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue