mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 22:29:39 -06:00
Support frame and video dumping from VideoCommon
This commit is contained in:
@ -66,22 +66,6 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format)
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width,
|
||||
int virtual_height, unsigned int level)
|
||||
{
|
||||
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
||||
return false;
|
||||
int width = std::max(virtual_width >> level, 1);
|
||||
int height = std::max(virtual_height >> level, 1);
|
||||
std::vector<u8> data(width * height * 4);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(textarget, tex);
|
||||
glGetTexImage(textarget, level, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
|
||||
OGLTexture::SetStage();
|
||||
|
||||
return TextureToPng(data.data(), width * 4, filename, width, height, true);
|
||||
}
|
||||
|
||||
OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
|
||||
{
|
||||
glGenTextures(1, &m_texId);
|
||||
@ -164,15 +148,73 @@ void OGLTexture::Bind(unsigned int stage)
|
||||
}
|
||||
}
|
||||
|
||||
bool OGLTexture::Save(const std::string& filename, unsigned int level)
|
||||
std::optional<AbstractTexture::RawTextureInfo> OGLTexture::MapFullImpl()
|
||||
{
|
||||
// We can't dump compressed textures currently (it would mean drawing them to a RGBA8
|
||||
// framebuffer, and saving that). TextureCache does not call Save for custom textures
|
||||
// anyway, so this is fine for now.
|
||||
_assert_(m_config.format == AbstractTextureFormat::RGBA8);
|
||||
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
||||
return {};
|
||||
|
||||
return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, m_texId, m_config.width, m_config.height,
|
||||
level);
|
||||
m_staging_data.reserve(m_config.width * m_config.height * 4);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data());
|
||||
OGLTexture::SetStage();
|
||||
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_data.data()),
|
||||
m_config.width * 4, m_config.width, m_config.height};
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> OGLTexture::MapRegionImpl(u32 level, u32 x, u32 y,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
||||
return {};
|
||||
m_staging_data.reserve(m_config.width * m_config.height * 4);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
if (g_ogl_config.bSupportTextureSubImage)
|
||||
{
|
||||
glGetTextureSubImage(GL_TEXTURE_2D_ARRAY, level, GLint(x), GLint(y), 0, GLsizei(width),
|
||||
GLsizei(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, GLsizei(width * height * 4),
|
||||
m_staging_data.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
MapRegionSlow(level, x, y, width, height);
|
||||
}
|
||||
OGLTexture::SetStage();
|
||||
return AbstractTexture::RawTextureInfo{m_staging_data.data(), width * 4, width, height};
|
||||
}
|
||||
|
||||
void OGLTexture::MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data());
|
||||
|
||||
// Now copy the region out of the staging data
|
||||
|
||||
const u32 partial_stride = width * 4;
|
||||
|
||||
std::vector<u8> partial_data;
|
||||
partial_data.resize(partial_stride * height);
|
||||
|
||||
const u32 staging_stride = m_config.width * 4;
|
||||
const u32 x_offset = x * 4;
|
||||
|
||||
auto staging_location = m_staging_data.begin() + staging_stride * y;
|
||||
auto partial_location = partial_data.begin();
|
||||
|
||||
for (size_t i = 0; i < height; ++i)
|
||||
{
|
||||
auto starting_location = staging_location + x_offset;
|
||||
std::copy(starting_location, starting_location + partial_stride, partial_location);
|
||||
staging_location += staging_stride;
|
||||
partial_location += partial_stride;
|
||||
}
|
||||
|
||||
// Now swap the region back in for the staging data
|
||||
m_staging_data.swap(partial_data);
|
||||
}
|
||||
|
||||
void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Common/GL/GLUtil.h"
|
||||
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
@ -17,7 +19,6 @@ public:
|
||||
~OGLTexture();
|
||||
|
||||
void Bind(unsigned int stage) override;
|
||||
bool Save(const std::string& filename, unsigned int level) override;
|
||||
|
||||
void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
@ -32,8 +33,14 @@ public:
|
||||
static void SetStage();
|
||||
|
||||
private:
|
||||
std::optional<RawTextureInfo> MapFullImpl() override;
|
||||
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||
u32 height) override;
|
||||
void MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height);
|
||||
|
||||
GLuint m_texId;
|
||||
GLuint m_framebuffer = 0;
|
||||
std::vector<u8> m_staging_data;
|
||||
};
|
||||
|
||||
} // namespace OGL
|
||||
|
@ -460,6 +460,7 @@ Renderer::Renderer()
|
||||
GLExtensions::Supports("GL_EXT_copy_image") ||
|
||||
GLExtensions::Supports("GL_OES_copy_image")) &&
|
||||
!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_COPYIMAGE);
|
||||
g_ogl_config.bSupportTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image");
|
||||
|
||||
// Desktop OpenGL supports the binding layout if it supports 420pack
|
||||
// OpenGL ES 3.1 supports it implicitly without an extension
|
||||
@ -792,7 +793,7 @@ Renderer::Renderer()
|
||||
Renderer::~Renderer()
|
||||
{
|
||||
FlushFrameDump();
|
||||
FinishFrameData();
|
||||
//FinishFrameData();
|
||||
DestroyFrameDumpResources();
|
||||
}
|
||||
|
||||
@ -1361,7 +1362,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
|
||||
|
||||
// The FlushFrameDump call here is necessary even after frame dumping is stopped.
|
||||
// If left out, screenshots are "one frame" behind, as an extra frame is dumped and buffered.
|
||||
FlushFrameDump();
|
||||
/*FlushFrameDump();
|
||||
if (IsFrameDumping())
|
||||
{
|
||||
// Currently, we only use the off-screen buffer as a frame dump source if full-resolution
|
||||
@ -1378,7 +1379,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
|
||||
// GL_READ_FRAMEBUFFER is set by GL_FRAMEBUFFER in DrawFrame -> Draw{EFB,VirtualXFB,RealXFB}.
|
||||
DumpFrame(flipped_trc, ticks);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Finish up the current frame, print some stats
|
||||
|
||||
@ -1510,7 +1511,7 @@ void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
|
||||
void Renderer::FlushFrameDump()
|
||||
{
|
||||
if (!m_last_frame_exported)
|
||||
/*if (!m_last_frame_exported)
|
||||
return;
|
||||
|
||||
FinishFrameData();
|
||||
@ -1521,7 +1522,7 @@ void Renderer::FlushFrameDump()
|
||||
DumpFrameData(reinterpret_cast<u8*>(data), m_last_frame_width[0], m_last_frame_height[0],
|
||||
m_last_frame_width[0] * 4, m_last_frame_state, true);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
m_last_frame_exported = false;
|
||||
m_last_frame_exported = false;*/
|
||||
}
|
||||
|
||||
void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
|
||||
|
@ -59,6 +59,7 @@ struct VideoConfig
|
||||
bool bSupportsImageLoadStore;
|
||||
bool bSupportsAniso;
|
||||
bool bSupportsBitfield;
|
||||
bool bSupportTextureSubImage;
|
||||
|
||||
const char* gl_vendor;
|
||||
const char* gl_renderer;
|
||||
|
Reference in New Issue
Block a user