video_core: Implement frame dumping
Two PBOs are used to speed up pixel copying process. To avoid getting the wrong speed/FPS, a new parameter is added to DrawScreens about whether to increase the frame count.
This commit is contained in:
parent
778cc68114
commit
0224ae13c4
|
@ -12,7 +12,9 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
#include "core/dumping/backend.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
#include "core/hw/gpu.h"
|
#include "core/hw/gpu.h"
|
||||||
#include "core/hw/hw.h"
|
#include "core/hw/hw.h"
|
||||||
#include "core/hw/lcd.h"
|
#include "core/hw/lcd.h"
|
||||||
|
@ -204,7 +206,38 @@ void RendererOpenGL::SwapBuffers() {
|
||||||
VideoCore::g_renderer_screenshot_requested = false;
|
VideoCore::g_renderer_screenshot_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cleanup_video_dumping.exchange(false)) {
|
||||||
|
ReleaseVideoDumpingGLObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Core::System::GetInstance().VideoDumper().IsDumping()) {
|
||||||
|
if (prepare_video_dumping.exchange(false)) {
|
||||||
|
InitVideoDumpingGLObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& layout = Core::System::GetInstance().VideoDumper().GetLayout();
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, frame_dumping_framebuffer.handle);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_dumping_framebuffer.handle);
|
||||||
|
DrawScreens(layout);
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, frame_dumping_pbos[current_pbo].handle);
|
||||||
|
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, frame_dumping_pbos[next_pbo].handle);
|
||||||
|
|
||||||
|
GLubyte* pixels = static_cast<GLubyte*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
|
||||||
|
VideoDumper::VideoFrame frame_data{layout.width, layout.height, pixels};
|
||||||
|
Core::System::GetInstance().VideoDumper().AddVideoFrame(frame_data);
|
||||||
|
|
||||||
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
current_pbo = (current_pbo + 1) % 2;
|
||||||
|
next_pbo = (current_pbo + 1) % 2;
|
||||||
|
}
|
||||||
|
|
||||||
DrawScreens(render_window.GetFramebufferLayout());
|
DrawScreens(render_window.GetFramebufferLayout());
|
||||||
|
m_current_frame++;
|
||||||
|
|
||||||
Core::System::GetInstance().perf_stats.EndSystemFrame();
|
Core::System::GetInstance().perf_stats.EndSystemFrame();
|
||||||
|
|
||||||
|
@ -634,13 +667,49 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
|
||||||
(float)bottom_screen.GetHeight());
|
(float)bottom_screen.GetHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_current_frame++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the framerate
|
/// Updates the framerate
|
||||||
void RendererOpenGL::UpdateFramerate() {}
|
void RendererOpenGL::UpdateFramerate() {}
|
||||||
|
|
||||||
|
void RendererOpenGL::PrepareVideoDumping() {
|
||||||
|
prepare_video_dumping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::CleanupVideoDumping() {
|
||||||
|
cleanup_video_dumping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::InitVideoDumpingGLObjects() {
|
||||||
|
const auto& layout = Core::System::GetInstance().VideoDumper().GetLayout();
|
||||||
|
|
||||||
|
frame_dumping_framebuffer.Create();
|
||||||
|
glGenRenderbuffers(1, &frame_dumping_renderbuffer);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, frame_dumping_renderbuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_dumping_framebuffer.handle);
|
||||||
|
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||||
|
frame_dumping_renderbuffer);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
for (auto& buffer : frame_dumping_pbos) {
|
||||||
|
buffer.Create();
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer.handle);
|
||||||
|
glBufferData(GL_PIXEL_PACK_BUFFER, layout.width * layout.height * 4, nullptr,
|
||||||
|
GL_STREAM_READ);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::ReleaseVideoDumpingGLObjects() {
|
||||||
|
frame_dumping_framebuffer.Release();
|
||||||
|
glDeleteRenderbuffers(1, &frame_dumping_renderbuffer);
|
||||||
|
|
||||||
|
for (auto& buffer : frame_dumping_pbos) {
|
||||||
|
buffer.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char* GetSource(GLenum source) {
|
static const char* GetSource(GLenum source) {
|
||||||
#define RET(s) \
|
#define RET(s) \
|
||||||
case GL_DEBUG_SOURCE_##s: \
|
case GL_DEBUG_SOURCE_##s: \
|
||||||
|
|
|
@ -50,6 +50,12 @@ public:
|
||||||
/// Shutdown the renderer
|
/// Shutdown the renderer
|
||||||
void ShutDown() override;
|
void ShutDown() override;
|
||||||
|
|
||||||
|
/// Prepares for video dumping (e.g. create necessary buffers, etc)
|
||||||
|
void PrepareVideoDumping() override;
|
||||||
|
|
||||||
|
/// Cleans up after video dumping is ended
|
||||||
|
void CleanupVideoDumping() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitOpenGLObjects();
|
void InitOpenGLObjects();
|
||||||
void ReloadSampler();
|
void ReloadSampler();
|
||||||
|
@ -69,6 +75,9 @@ private:
|
||||||
// Fills active OpenGL texture with the given RGB color.
|
// Fills active OpenGL texture with the given RGB color.
|
||||||
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
|
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
|
||||||
|
|
||||||
|
void InitVideoDumpingGLObjects();
|
||||||
|
void ReleaseVideoDumpingGLObjects();
|
||||||
|
|
||||||
OpenGLState state;
|
OpenGLState state;
|
||||||
|
|
||||||
// OpenGL object IDs
|
// OpenGL object IDs
|
||||||
|
@ -94,6 +103,20 @@ private:
|
||||||
// Shader attribute input indices
|
// Shader attribute input indices
|
||||||
GLuint attrib_position;
|
GLuint attrib_position;
|
||||||
GLuint attrib_tex_coord;
|
GLuint attrib_tex_coord;
|
||||||
|
|
||||||
|
// Frame dumping
|
||||||
|
OGLFramebuffer frame_dumping_framebuffer;
|
||||||
|
GLuint frame_dumping_renderbuffer;
|
||||||
|
|
||||||
|
// Whether prepare/cleanup video dumping has been requested.
|
||||||
|
// They will be executed on next frame.
|
||||||
|
std::atomic_bool prepare_video_dumping = false;
|
||||||
|
std::atomic_bool cleanup_video_dumping = false;
|
||||||
|
|
||||||
|
// PBOs used to dump frames faster
|
||||||
|
std::array<OGLBuffer, 2> frame_dumping_pbos;
|
||||||
|
GLuint current_pbo = 1;
|
||||||
|
GLuint next_pbo = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
|
Reference in New Issue