Merge pull request #4423 from degasus/framedump
Framedump: Use an object for the framedumping state.
This commit is contained in:
commit
3924a99942
|
@ -824,8 +824,9 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||||
D3D11_MAPPED_SUBRESOURCE map;
|
D3D11_MAPPED_SUBRESOURCE map;
|
||||||
D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ, 0, &map);
|
D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ, 0, &map);
|
||||||
|
|
||||||
|
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
||||||
DumpFrameData(reinterpret_cast<const u8*>(map.pData), source_width, source_height, map.RowPitch,
|
DumpFrameData(reinterpret_cast<const u8*>(map.pData), source_width, source_height, map.RowPitch,
|
||||||
ticks);
|
state);
|
||||||
FinishFrameData();
|
FinishFrameData();
|
||||||
|
|
||||||
D3D::context->Unmap(s_screenshot_texture, 0);
|
D3D::context->Unmap(s_screenshot_texture, 0);
|
||||||
|
|
|
@ -777,8 +777,9 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
||||||
D3D12_RANGE read_range = {0, dst_location.PlacedFootprint.Footprint.RowPitch * source_height};
|
D3D12_RANGE read_range = {0, dst_location.PlacedFootprint.Footprint.RowPitch * source_height};
|
||||||
CheckHR(s_screenshot_texture->Map(0, &read_range, &screenshot_texture_map));
|
CheckHR(s_screenshot_texture->Map(0, &read_range, &screenshot_texture_map));
|
||||||
|
|
||||||
|
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
||||||
DumpFrameData(reinterpret_cast<const u8*>(screenshot_texture_map), source_width, source_height,
|
DumpFrameData(reinterpret_cast<const u8*>(screenshot_texture_map), source_width, source_height,
|
||||||
dst_location.PlacedFootprint.Footprint.RowPitch, ticks);
|
dst_location.PlacedFootprint.Footprint.RowPitch, state);
|
||||||
FinishFrameData();
|
FinishFrameData();
|
||||||
|
|
||||||
D3D12_RANGE write_range = {};
|
D3D12_RANGE write_range = {};
|
||||||
|
|
|
@ -1455,8 +1455,9 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||||
glReadPixels(flipped_trc.left, flipped_trc.bottom, flipped_trc.GetWidth(),
|
glReadPixels(flipped_trc.left, flipped_trc.bottom, flipped_trc.GetWidth(),
|
||||||
flipped_trc.GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, image.data());
|
flipped_trc.GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, image.data());
|
||||||
|
|
||||||
|
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
||||||
DumpFrameData(image.data(), flipped_trc.GetWidth(), flipped_trc.GetHeight(),
|
DumpFrameData(image.data(), flipped_trc.GetWidth(), flipped_trc.GetHeight(),
|
||||||
flipped_trc.GetWidth() * 4, ticks, true);
|
flipped_trc.GetWidth() * 4, state, true);
|
||||||
FinishFrameData();
|
FinishFrameData();
|
||||||
}
|
}
|
||||||
// Finish up the current frame, print some stats
|
// Finish up the current frame, print some stats
|
||||||
|
|
|
@ -125,7 +125,8 @@ void SWRenderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||||
// Save screenshot
|
// Save screenshot
|
||||||
if (IsFrameDumping())
|
if (IsFrameDumping())
|
||||||
{
|
{
|
||||||
DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, ticks);
|
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
||||||
|
DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, state);
|
||||||
FinishFrameData();
|
FinishFrameData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -747,10 +747,11 @@ bool Renderer::DrawFrameDump(const EFBRectangle& rc, u32 xfb_addr,
|
||||||
|
|
||||||
void Renderer::DumpFrame(u64 ticks)
|
void Renderer::DumpFrame(u64 ticks)
|
||||||
{
|
{
|
||||||
|
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
||||||
DumpFrameData(reinterpret_cast<const u8*>(m_frame_dump_readback_texture->GetMapPointer()),
|
DumpFrameData(reinterpret_cast<const u8*>(m_frame_dump_readback_texture->GetMapPointer()),
|
||||||
static_cast<int>(m_frame_dump_render_texture->GetWidth()),
|
static_cast<int>(m_frame_dump_render_texture->GetWidth()),
|
||||||
static_cast<int>(m_frame_dump_render_texture->GetHeight()),
|
static_cast<int>(m_frame_dump_render_texture->GetHeight()),
|
||||||
static_cast<int>(m_frame_dump_readback_texture->GetRowStride()), ticks);
|
static_cast<int>(m_frame_dump_readback_texture->GetRowStride()), state);
|
||||||
FinishFrameData();
|
FinishFrameData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,9 @@ extern "C" {
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/HW/VideoInterface.h" //for TargetRefreshRate
|
#include "Core/HW/VideoInterface.h" //for TargetRefreshRate
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
|
|
||||||
#include "VideoCommon/AVIDump.h"
|
#include "VideoCommon/AVIDump.h"
|
||||||
|
#include "VideoCommon/OnScreenDisplay.h"
|
||||||
#include "VideoCommon/VideoConfig.h"
|
#include "VideoCommon/VideoConfig.h"
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1)
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1)
|
||||||
|
@ -42,16 +44,10 @@ static int s_height;
|
||||||
static u64 s_last_frame;
|
static u64 s_last_frame;
|
||||||
static bool s_last_frame_is_valid = false;
|
static bool s_last_frame_is_valid = false;
|
||||||
static bool s_start_dumping = false;
|
static bool s_start_dumping = false;
|
||||||
static bool s_stop_dumping = false;
|
|
||||||
static u64 s_last_pts;
|
static u64 s_last_pts;
|
||||||
static int s_current_width;
|
|
||||||
static int s_current_height;
|
|
||||||
static int s_file_index = 0;
|
static int s_file_index = 0;
|
||||||
static const u8* s_stored_frame_data;
|
static int s_savestate_index = 0;
|
||||||
static int s_stored_frame_width;
|
static int s_last_savestate_index = 0;
|
||||||
static int s_stored_frame_height;
|
|
||||||
static int s_stored_frame_stride;
|
|
||||||
static u64 s_stored_frame_ticks;
|
|
||||||
|
|
||||||
static void InitAVCodec()
|
static void InitAVCodec()
|
||||||
{
|
{
|
||||||
|
@ -69,18 +65,17 @@ bool AVIDump::Start(int w, int h)
|
||||||
|
|
||||||
s_width = w;
|
s_width = w;
|
||||||
s_height = h;
|
s_height = h;
|
||||||
s_current_width = w;
|
|
||||||
s_current_height = h;
|
|
||||||
|
|
||||||
s_last_frame_is_valid = false;
|
s_last_frame_is_valid = false;
|
||||||
s_last_pts = 0;
|
s_last_pts = 0;
|
||||||
|
|
||||||
s_stop_dumping = false;
|
|
||||||
|
|
||||||
InitAVCodec();
|
InitAVCodec();
|
||||||
bool success = CreateFile();
|
bool success = CreateFile();
|
||||||
if (!success)
|
if (!success)
|
||||||
|
{
|
||||||
CloseFile();
|
CloseFile();
|
||||||
|
OSD::AddMessage("AVIDump Start failed");
|
||||||
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +155,9 @@ bool AVIDump::CreateFile()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OSD::AddMessage(StringFromFormat("Dumping Frames to \"%s\" (%dx%d)", s_format_context->filename,
|
||||||
|
s_width, s_height));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,24 +168,24 @@ static void PreparePacket(AVPacket* pkt)
|
||||||
pkt->size = 0;
|
pkt->size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVIDump::AddFrame(const u8* data, int width, int height, int stride, u64 ticks)
|
void AVIDump::AddFrame(const u8* data, int width, int height, int stride, const Frame& state)
|
||||||
{
|
{
|
||||||
// Store current frame data in case frame dumping stops before next frame update,
|
// Assume that the timing is valid, if the savestate id of the new frame
|
||||||
// but make sure that you don't store the last stored frame and check the resolution upon
|
// doesn't match the last one.
|
||||||
// closing the file or else you store recursion, and dolphins don't like recursion.
|
if (state.savestate_index != s_last_savestate_index)
|
||||||
if (!s_stop_dumping)
|
|
||||||
{
|
{
|
||||||
StoreFrameData(data, width, height, stride, ticks);
|
s_last_savestate_index = state.savestate_index;
|
||||||
CheckResolution(width, height);
|
s_last_frame_is_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckResolution(width, height);
|
||||||
s_src_frame->data[0] = const_cast<u8*>(data);
|
s_src_frame->data[0] = const_cast<u8*>(data);
|
||||||
s_src_frame->linesize[0] = stride;
|
s_src_frame->linesize[0] = stride;
|
||||||
s_src_frame->format = s_pix_fmt;
|
s_src_frame->format = s_pix_fmt;
|
||||||
s_src_frame->width = s_width;
|
s_src_frame->width = s_width;
|
||||||
s_src_frame->height = s_height;
|
s_src_frame->height = s_height;
|
||||||
|
|
||||||
// Convert image from {BGR24, RGBA} to desired pixel format, and scale to initial
|
// Convert image from {BGR24, RGBA} to desired pixel format
|
||||||
// width and height
|
|
||||||
if ((s_sws_context =
|
if ((s_sws_context =
|
||||||
sws_getCachedContext(s_sws_context, width, height, s_pix_fmt, s_width, s_height,
|
sws_getCachedContext(s_sws_context, width, height, s_pix_fmt, s_width, s_height,
|
||||||
s_stream->codec->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr)))
|
s_stream->codec->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr)))
|
||||||
|
@ -208,26 +206,25 @@ void AVIDump::AddFrame(const u8* data, int width, int height, int stride, u64 ti
|
||||||
// incorrectly.
|
// incorrectly.
|
||||||
if (!s_last_frame_is_valid)
|
if (!s_last_frame_is_valid)
|
||||||
{
|
{
|
||||||
s_last_frame = ticks;
|
s_last_frame = state.ticks;
|
||||||
s_last_frame_is_valid = true;
|
s_last_frame_is_valid = true;
|
||||||
}
|
}
|
||||||
if (!s_start_dumping && Movie::GetCurrentFrame() < 1)
|
if (!s_start_dumping && state.first_frame)
|
||||||
{
|
{
|
||||||
delta = ticks;
|
delta = state.ticks;
|
||||||
last_pts = AV_NOPTS_VALUE;
|
last_pts = AV_NOPTS_VALUE;
|
||||||
s_start_dumping = true;
|
s_start_dumping = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
delta = ticks - s_last_frame;
|
delta = state.ticks - s_last_frame;
|
||||||
last_pts = (s_last_pts * s_stream->codec->time_base.den) / SystemTimers::GetTicksPerSecond();
|
last_pts = (s_last_pts * s_stream->codec->time_base.den) / state.ticks_per_second;
|
||||||
}
|
}
|
||||||
u64 pts_in_ticks = s_last_pts + delta;
|
u64 pts_in_ticks = s_last_pts + delta;
|
||||||
s_scaled_frame->pts =
|
s_scaled_frame->pts = (pts_in_ticks * s_stream->codec->time_base.den) / state.ticks_per_second;
|
||||||
(pts_in_ticks * s_stream->codec->time_base.den) / SystemTimers::GetTicksPerSecond();
|
|
||||||
if (s_scaled_frame->pts != last_pts)
|
if (s_scaled_frame->pts != last_pts)
|
||||||
{
|
{
|
||||||
s_last_frame = ticks;
|
s_last_frame = state.ticks;
|
||||||
s_last_pts = pts_in_ticks;
|
s_last_pts = pts_in_ticks;
|
||||||
error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet);
|
error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet);
|
||||||
}
|
}
|
||||||
|
@ -259,10 +256,6 @@ void AVIDump::AddFrame(const u8* data, int width, int height, int stride, u64 ti
|
||||||
|
|
||||||
void AVIDump::Stop()
|
void AVIDump::Stop()
|
||||||
{
|
{
|
||||||
s_stop_dumping = true;
|
|
||||||
// Write the last stored frame just in case frame dumping stops before the next frame update
|
|
||||||
AddFrame(s_stored_frame_data, s_stored_frame_width, s_stored_frame_height, s_stored_frame_stride,
|
|
||||||
s_stored_frame_ticks);
|
|
||||||
av_write_trailer(s_format_context);
|
av_write_trailer(s_format_context);
|
||||||
CloseFile();
|
CloseFile();
|
||||||
s_file_index = 0;
|
s_file_index = 0;
|
||||||
|
@ -302,7 +295,7 @@ void AVIDump::CloseFile()
|
||||||
|
|
||||||
void AVIDump::DoState()
|
void AVIDump::DoState()
|
||||||
{
|
{
|
||||||
s_last_frame_is_valid = false;
|
s_savestate_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVIDump::CheckResolution(int width, int height)
|
void AVIDump::CheckResolution(int width, int height)
|
||||||
|
@ -312,22 +305,21 @@ void AVIDump::CheckResolution(int width, int height)
|
||||||
// (possibly width as well, but no examples known) to have a value of zero. This can occur as the
|
// (possibly width as well, but no examples known) to have a value of zero. This can occur as the
|
||||||
// VI is able to be set to a zero value for height/width to disable output. If this is the case,
|
// VI is able to be set to a zero value for height/width to disable output. If this is the case,
|
||||||
// simply keep the last known resolution of the video for the added frame.
|
// simply keep the last known resolution of the video for the added frame.
|
||||||
if ((width != s_current_width || height != s_current_height) && (width > 0 && height > 0))
|
if ((width != s_width || height != s_height) && (width > 0 && height > 0))
|
||||||
{
|
{
|
||||||
int temp_file_index = s_file_index;
|
int temp_file_index = s_file_index;
|
||||||
Stop();
|
Stop();
|
||||||
s_file_index = temp_file_index + 1;
|
s_file_index = temp_file_index + 1;
|
||||||
Start(width, height);
|
Start(width, height);
|
||||||
s_current_width = width;
|
|
||||||
s_current_height = height;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVIDump::StoreFrameData(const u8* data, int width, int height, int stride, u64 ticks)
|
AVIDump::Frame AVIDump::FetchState(u64 ticks)
|
||||||
{
|
{
|
||||||
s_stored_frame_data = data;
|
Frame state;
|
||||||
s_stored_frame_width = width;
|
state.ticks = ticks;
|
||||||
s_stored_frame_height = height;
|
state.first_frame = Movie::GetCurrentFrame() < 1;
|
||||||
s_stored_frame_stride = stride;
|
state.ticks_per_second = SystemTimers::GetTicksPerSecond();
|
||||||
s_stored_frame_ticks = ticks;
|
state.savestate_index = s_savestate_index;
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,24 @@ private:
|
||||||
static bool CreateFile();
|
static bool CreateFile();
|
||||||
static void CloseFile();
|
static void CloseFile();
|
||||||
static void CheckResolution(int width, int height);
|
static void CheckResolution(int width, int height);
|
||||||
static void StoreFrameData(const u8* data, int width, int height, int stride, u64 ticks);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct Frame
|
||||||
|
{
|
||||||
|
u64 ticks = 0;
|
||||||
|
u32 ticks_per_second = 0;
|
||||||
|
bool first_frame = false;
|
||||||
|
int savestate_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
static bool Start(int w, int h);
|
static bool Start(int w, int h);
|
||||||
static void AddFrame(const u8* data, int width, int height, int stride, u64 ticks);
|
static void AddFrame(const u8* data, int width, int height, int stride, const Frame& state);
|
||||||
static void Stop();
|
static void Stop();
|
||||||
static void DoState();
|
static void DoState();
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
||||||
|
static Frame FetchState(u64 ticks);
|
||||||
|
#else
|
||||||
|
static Frame FetchState(u64 ticks) { return {}; }
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
@ -660,14 +660,14 @@ bool Renderer::IsFrameDumping()
|
||||||
AVIDump::Stop();
|
AVIDump::Stop();
|
||||||
std::vector<u8>().swap(m_frame_data);
|
std::vector<u8>().swap(m_frame_data);
|
||||||
m_AVI_dumping = false;
|
m_AVI_dumping = false;
|
||||||
OSD::AddMessage("Stop dumping frames", 2000);
|
OSD::AddMessage("Stop dumping frames");
|
||||||
}
|
}
|
||||||
m_last_frame_dumped = false;
|
m_last_frame_dumped = false;
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, u64 ticks,
|
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state,
|
||||||
bool swap_upside_down)
|
bool swap_upside_down)
|
||||||
{
|
{
|
||||||
if (w == 0 || h == 0)
|
if (w == 0 || h == 0)
|
||||||
|
@ -698,20 +698,10 @@ void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, u64 ticks
|
||||||
if (!m_last_frame_dumped)
|
if (!m_last_frame_dumped)
|
||||||
{
|
{
|
||||||
m_AVI_dumping = AVIDump::Start(w, h);
|
m_AVI_dumping = AVIDump::Start(w, h);
|
||||||
if (!m_AVI_dumping)
|
|
||||||
{
|
|
||||||
OSD::AddMessage("AVIDump Start failed", 2000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OSD::AddMessage(StringFromFormat("Dumping Frames to \"%sframedump0.avi\" (%dx%d RGB24)",
|
|
||||||
File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), w, h),
|
|
||||||
2000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (m_AVI_dumping)
|
if (m_AVI_dumping)
|
||||||
{
|
{
|
||||||
AVIDump::AddFrame(m_frame_data.data(), w, h, stride, ticks);
|
AVIDump::AddFrame(m_frame_data.data(), w, h, stride, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_frame_dumped = true;
|
m_last_frame_dumped = true;
|
||||||
|
|
|
@ -147,7 +147,7 @@ protected:
|
||||||
static void RecordVideoMemory();
|
static void RecordVideoMemory();
|
||||||
|
|
||||||
bool IsFrameDumping();
|
bool IsFrameDumping();
|
||||||
void DumpFrameData(const u8* data, int w, int h, int stride, u64 ticks,
|
void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state,
|
||||||
bool swap_upside_down = false);
|
bool swap_upside_down = false);
|
||||||
void FinishFrameData();
|
void FinishFrameData();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue