Support frame and video dumping from VideoCommon
This commit is contained in:
parent
79387dddb2
commit
a9f0d1783b
|
@ -80,6 +80,7 @@ DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_conf
|
||||||
DXTexture::~DXTexture()
|
DXTexture::~DXTexture()
|
||||||
{
|
{
|
||||||
m_texture->Release();
|
m_texture->Release();
|
||||||
|
SAFE_RELEASE(m_staging_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
D3DTexture2D* DXTexture::GetRawTexIdentifier() const
|
D3DTexture2D* DXTexture::GetRawTexIdentifier() const
|
||||||
|
@ -92,48 +93,72 @@ void DXTexture::Bind(unsigned int stage)
|
||||||
D3D::stateman->SetTexture(stage, m_texture->GetSRV());
|
D3D::stateman->SetTexture(stage, m_texture->GetSRV());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DXTexture::Save(const std::string& filename, unsigned int level)
|
std::optional<AbstractTexture::RawTextureInfo> DXTexture::MapFullImpl()
|
||||||
{
|
{
|
||||||
// We can't dump compressed textures currently (it would mean drawing them to a RGBA8
|
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, m_config.width,
|
||||||
// framebuffer, and saving that). TextureCache does not call Save for custom textures
|
m_config.height, 1, 1, 0, D3D11_USAGE_STAGING,
|
||||||
// anyway, so this is fine for now.
|
D3D11_CPU_ACCESS_READ);
|
||||||
_assert_(m_config.format == AbstractTextureFormat::RGBA8);
|
|
||||||
|
|
||||||
// Create a staging/readback texture with the dimensions of the specified mip level.
|
HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture);
|
||||||
u32 mip_width = std::max(m_config.width >> level, 1u);
|
|
||||||
u32 mip_height = std::max(m_config.height >> level, 1u);
|
|
||||||
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, mip_width, mip_height, 1,
|
|
||||||
1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ);
|
|
||||||
|
|
||||||
ID3D11Texture2D* staging_texture;
|
|
||||||
HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &staging_texture);
|
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
|
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the selected mip level to the staging texture.
|
// Copy the selected data to the staging texture
|
||||||
CD3D11_BOX src_box(0, 0, 0, mip_width, mip_height, 1);
|
D3D::context->CopyResource(m_staging_texture, m_texture->GetTex());
|
||||||
D3D::context->CopySubresourceRegion(staging_texture, 0, 0, 0, 0, m_texture->GetTex(),
|
|
||||||
|
// Map the staging texture to client memory, and encode it as a .png image.
|
||||||
|
D3D11_MAPPED_SUBRESOURCE map;
|
||||||
|
hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(map.pData), map.RowPitch,
|
||||||
|
m_config.width, m_config.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AbstractTexture::RawTextureInfo> DXTexture::MapRegionImpl(u32 level, u32 x, u32 y,
|
||||||
|
u32 width, u32 height)
|
||||||
|
{
|
||||||
|
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, 1, 0,
|
||||||
|
D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ);
|
||||||
|
|
||||||
|
HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the selected data to the staging texture
|
||||||
|
CD3D11_BOX src_box(x, y, 0, width, height, 1);
|
||||||
|
D3D::context->CopySubresourceRegion(m_staging_texture, 0, 0, 0, 0, m_texture->GetTex(),
|
||||||
D3D11CalcSubresource(level, 0, m_config.levels), &src_box);
|
D3D11CalcSubresource(level, 0, m_config.levels), &src_box);
|
||||||
|
|
||||||
// Map the staging texture to client memory, and encode it as a .png image.
|
// Map the staging texture to client memory, and encode it as a .png image.
|
||||||
D3D11_MAPPED_SUBRESOURCE map;
|
D3D11_MAPPED_SUBRESOURCE map;
|
||||||
hr = D3D::context->Map(staging_texture, 0, D3D11_MAP_READ, 0, &map);
|
hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
|
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
|
||||||
staging_texture->Release();
|
return {};
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encode_result =
|
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(map.pData), map.RowPitch,
|
||||||
TextureToPng(reinterpret_cast<u8*>(map.pData), map.RowPitch, filename, mip_width, mip_height);
|
m_config.width, m_config.height};
|
||||||
D3D::context->Unmap(staging_texture, 0);
|
}
|
||||||
staging_texture->Release();
|
|
||||||
|
|
||||||
return encode_result;
|
void DXTexture::Unmap()
|
||||||
|
{
|
||||||
|
if (!m_staging_texture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
D3D::context->Unmap(m_staging_texture, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
~DXTexture();
|
~DXTexture();
|
||||||
|
|
||||||
void Bind(unsigned int stage) override;
|
void Bind(unsigned int stage) override;
|
||||||
bool Save(const std::string& filename, unsigned int level) override;
|
void Unmap() override;
|
||||||
|
|
||||||
void CopyRectangleFromTexture(const AbstractTexture* source,
|
void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||||
const MathUtil::Rectangle<int>& srcrect,
|
const MathUtil::Rectangle<int>& srcrect,
|
||||||
|
@ -30,7 +30,12 @@ public:
|
||||||
D3DTexture2D* GetRawTexIdentifier() const;
|
D3DTexture2D* GetRawTexIdentifier() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::optional<RawTextureInfo> MapFullImpl() override;
|
||||||
|
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||||
|
u32 height) override;
|
||||||
|
|
||||||
D3DTexture2D* m_texture;
|
D3DTexture2D* m_texture;
|
||||||
|
ID3D11Texture2D* m_staging_texture = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace DX11
|
} // namespace DX11
|
||||||
|
|
|
@ -659,29 +659,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
|
||||||
BlitScreen(source_rc, targetRc, xfb_texture->GetRawTexIdentifier(), xfb_texture->config.width,
|
BlitScreen(source_rc, targetRc, xfb_texture->GetRawTexIdentifier(), xfb_texture->config.width,
|
||||||
xfb_texture->config.height, Gamma);
|
xfb_texture->config.height, Gamma);
|
||||||
|
|
||||||
// Dump frames
|
|
||||||
if (IsFrameDumping())
|
|
||||||
{
|
|
||||||
if (!s_screenshot_texture)
|
|
||||||
CreateScreenshotTexture();
|
|
||||||
|
|
||||||
D3D11_BOX source_box = GetScreenshotSourceBox(targetRc);
|
|
||||||
unsigned int source_width = source_box.right - source_box.left;
|
|
||||||
unsigned int source_height = source_box.bottom - source_box.top;
|
|
||||||
D3D::context->CopySubresourceRegion(s_screenshot_texture, 0, 0, 0, 0,
|
|
||||||
D3D::GetBackBuffer()->GetTex(), 0, &source_box);
|
|
||||||
|
|
||||||
D3D11_MAPPED_SUBRESOURCE 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,
|
|
||||||
state);
|
|
||||||
FinishFrameData();
|
|
||||||
|
|
||||||
D3D::context->Unmap(s_screenshot_texture, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset viewport for drawing text
|
// Reset viewport for drawing text
|
||||||
D3D11_VIEWPORT vp =
|
D3D11_VIEWPORT vp =
|
||||||
CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetBackbufferWidth(), (float)GetBackbufferHeight());
|
CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetBackbufferWidth(), (float)GetBackbufferHeight());
|
||||||
|
|
|
@ -66,22 +66,6 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format)
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // 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)
|
OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
|
||||||
{
|
{
|
||||||
glGenTextures(1, &m_texId);
|
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
|
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
||||||
// framebuffer, and saving that). TextureCache does not call Save for custom textures
|
return {};
|
||||||
// anyway, so this is fine for now.
|
|
||||||
_assert_(m_config.format == AbstractTextureFormat::RGBA8);
|
|
||||||
|
|
||||||
return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, m_texId, m_config.width, m_config.height,
|
m_staging_data.reserve(m_config.width * m_config.height * 4);
|
||||||
level);
|
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,
|
void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/GL/GLUtil.h"
|
#include "Common/GL/GLUtil.h"
|
||||||
|
|
||||||
#include "VideoCommon/AbstractTexture.h"
|
#include "VideoCommon/AbstractTexture.h"
|
||||||
|
@ -17,7 +19,6 @@ public:
|
||||||
~OGLTexture();
|
~OGLTexture();
|
||||||
|
|
||||||
void Bind(unsigned int stage) override;
|
void Bind(unsigned int stage) override;
|
||||||
bool Save(const std::string& filename, unsigned int level) override;
|
|
||||||
|
|
||||||
void CopyRectangleFromTexture(const AbstractTexture* source,
|
void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||||
const MathUtil::Rectangle<int>& srcrect,
|
const MathUtil::Rectangle<int>& srcrect,
|
||||||
|
@ -32,8 +33,14 @@ public:
|
||||||
static void SetStage();
|
static void SetStage();
|
||||||
|
|
||||||
private:
|
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_texId;
|
||||||
GLuint m_framebuffer = 0;
|
GLuint m_framebuffer = 0;
|
||||||
|
std::vector<u8> m_staging_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OGL
|
} // namespace OGL
|
||||||
|
|
|
@ -460,6 +460,7 @@ Renderer::Renderer()
|
||||||
GLExtensions::Supports("GL_EXT_copy_image") ||
|
GLExtensions::Supports("GL_EXT_copy_image") ||
|
||||||
GLExtensions::Supports("GL_OES_copy_image")) &&
|
GLExtensions::Supports("GL_OES_copy_image")) &&
|
||||||
!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_COPYIMAGE);
|
!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
|
// Desktop OpenGL supports the binding layout if it supports 420pack
|
||||||
// OpenGL ES 3.1 supports it implicitly without an extension
|
// OpenGL ES 3.1 supports it implicitly without an extension
|
||||||
|
@ -792,7 +793,7 @@ Renderer::Renderer()
|
||||||
Renderer::~Renderer()
|
Renderer::~Renderer()
|
||||||
{
|
{
|
||||||
FlushFrameDump();
|
FlushFrameDump();
|
||||||
FinishFrameData();
|
//FinishFrameData();
|
||||||
DestroyFrameDumpResources();
|
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.
|
// 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.
|
// If left out, screenshots are "one frame" behind, as an extra frame is dumped and buffered.
|
||||||
FlushFrameDump();
|
/*FlushFrameDump();
|
||||||
if (IsFrameDumping())
|
if (IsFrameDumping())
|
||||||
{
|
{
|
||||||
// Currently, we only use the off-screen buffer as a frame dump source if full-resolution
|
// 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}.
|
// GL_READ_FRAMEBUFFER is set by GL_FRAMEBUFFER in DrawFrame -> Draw{EFB,VirtualXFB,RealXFB}.
|
||||||
DumpFrame(flipped_trc, ticks);
|
DumpFrame(flipped_trc, ticks);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// Finish up the current frame, print some stats
|
// Finish up the current frame, print some stats
|
||||||
|
|
||||||
|
@ -1510,7 +1511,7 @@ void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||||
|
|
||||||
void Renderer::FlushFrameDump()
|
void Renderer::FlushFrameDump()
|
||||||
{
|
{
|
||||||
if (!m_last_frame_exported)
|
/*if (!m_last_frame_exported)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FinishFrameData();
|
FinishFrameData();
|
||||||
|
@ -1521,7 +1522,7 @@ void Renderer::FlushFrameDump()
|
||||||
DumpFrameData(reinterpret_cast<u8*>(data), m_last_frame_width[0], m_last_frame_height[0],
|
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);
|
m_last_frame_width[0] * 4, m_last_frame_state, true);
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
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)
|
void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
|
||||||
|
|
|
@ -59,6 +59,7 @@ struct VideoConfig
|
||||||
bool bSupportsImageLoadStore;
|
bool bSupportsImageLoadStore;
|
||||||
bool bSupportsAniso;
|
bool bSupportsAniso;
|
||||||
bool bSupportsBitfield;
|
bool bSupportsBitfield;
|
||||||
|
bool bSupportTextureSubImage;
|
||||||
|
|
||||||
const char* gl_vendor;
|
const char* gl_vendor;
|
||||||
const char* gl_renderer;
|
const char* gl_renderer;
|
||||||
|
|
|
@ -47,14 +47,6 @@ void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64
|
||||||
{
|
{
|
||||||
SWOGLWindow::s_instance->ShowImage(texture, 1.0);
|
SWOGLWindow::s_instance->ShowImage(texture, 1.0);
|
||||||
|
|
||||||
// Save screenshot
|
|
||||||
if (IsFrameDumping())
|
|
||||||
{
|
|
||||||
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
|
||||||
//DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, state);
|
|
||||||
FinishFrameData();
|
|
||||||
}
|
|
||||||
|
|
||||||
OSD::DoCallbacks(OSD::CallbackType::OnFrame);
|
OSD::DoCallbacks(OSD::CallbackType::OnFrame);
|
||||||
|
|
||||||
DrawDebugText();
|
DrawDebugText();
|
||||||
|
|
|
@ -507,23 +507,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
|
||||||
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
||||||
UpdateDrawRectangle();
|
UpdateDrawRectangle();
|
||||||
|
|
||||||
// Render the frame dump image if enabled.
|
|
||||||
if (IsFrameDumping())
|
|
||||||
{
|
|
||||||
// If we haven't dumped a single frame yet, set up frame dumping.
|
|
||||||
if (!m_frame_dumping_active)
|
|
||||||
StartFrameDumping();
|
|
||||||
|
|
||||||
/* DrawFrameDump(scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height,
|
|
||||||
ticks);*/
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If frame dumping was previously enabled, flush all frames and remove the fence callback.
|
|
||||||
if (m_frame_dumping_active)
|
|
||||||
EndFrameDumping();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the worker thread is not still submitting a previous command buffer.
|
// Ensure the worker thread is not still submitting a previous command buffer.
|
||||||
// In other words, the last frame has been submitted (otherwise the next call would
|
// In other words, the last frame has been submitted (otherwise the next call would
|
||||||
// be a race, as the image may not have been consumed yet).
|
// be a race, as the image may not have been consumed yet).
|
||||||
|
@ -856,7 +839,7 @@ void Renderer::OnFrameDumpImageReady(VkFence fence)
|
||||||
|
|
||||||
void Renderer::WriteFrameDumpImage(size_t index)
|
void Renderer::WriteFrameDumpImage(size_t index)
|
||||||
{
|
{
|
||||||
FrameDumpImage& frame = m_frame_dump_images[index];
|
/*FrameDumpImage& frame = m_frame_dump_images[index];
|
||||||
_assert_(frame.pending);
|
_assert_(frame.pending);
|
||||||
|
|
||||||
// Check fence has been signaled.
|
// Check fence has been signaled.
|
||||||
|
@ -873,14 +856,14 @@ void Renderer::WriteFrameDumpImage(size_t index)
|
||||||
static_cast<int>(frame.readback_texture->GetHeight()),
|
static_cast<int>(frame.readback_texture->GetHeight()),
|
||||||
static_cast<int>(frame.readback_texture->GetRowStride()), frame.dump_state);
|
static_cast<int>(frame.readback_texture->GetRowStride()), frame.dump_state);
|
||||||
|
|
||||||
frame.pending = false;
|
frame.pending = false;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
StagingTexture2D* Renderer::PrepareFrameDumpImage(u32 width, u32 height, u64 ticks)
|
StagingTexture2D* Renderer::PrepareFrameDumpImage(u32 width, u32 height, u64 ticks)
|
||||||
{
|
{
|
||||||
// Ensure the last frame that was sent to the frame dump has completed encoding before we send
|
// Ensure the last frame that was sent to the frame dump has completed encoding before we send
|
||||||
// the next image to it.
|
// the next image to it.
|
||||||
FinishFrameData();
|
//FinishFrameData();
|
||||||
|
|
||||||
// If the last image hasn't been written to the frame dump yet, write it now.
|
// If the last image hasn't been written to the frame dump yet, write it now.
|
||||||
// This is necessary so that the worker thread is no more than one frame behind, and the pointer
|
// This is necessary so that the worker thread is no more than one frame behind, and the pointer
|
||||||
|
|
|
@ -113,23 +113,17 @@ void VKTexture::Bind(unsigned int stage)
|
||||||
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
|
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKTexture::Save(const std::string& filename, unsigned int level)
|
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapFullImpl()
|
||||||
{
|
{
|
||||||
_assert_(level < m_config.levels);
|
// No support for optimization of full copy
|
||||||
|
return MapRegionImpl(0, 0, 0, m_config.width, m_config.height);
|
||||||
|
}
|
||||||
|
|
||||||
// We can't dump compressed textures currently (it would mean drawing them to a RGBA8
|
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapRegionImpl(u32 level, u32 x, u32 y,
|
||||||
// framebuffer, and saving that). TextureCache does not call Save for custom textures
|
u32 width, u32 height)
|
||||||
// anyway, so this is fine for now.
|
{
|
||||||
_assert_(m_config.format == AbstractTextureFormat::RGBA8);
|
m_staging_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height,
|
||||||
|
TEXTURECACHE_TEXTURE_FORMAT);
|
||||||
// Determine dimensions of image we want to save.
|
|
||||||
u32 level_width = std::max(1u, m_config.width >> level);
|
|
||||||
u32 level_height = std::max(1u, m_config.height >> level);
|
|
||||||
|
|
||||||
// Use a temporary staging texture for the download. Certainly not optimal,
|
|
||||||
// but since we have to idle the GPU anyway it doesn't really matter.
|
|
||||||
std::unique_ptr<StagingTexture2D> staging_texture = StagingTexture2D::Create(
|
|
||||||
STAGING_BUFFER_TYPE_READBACK, level_width, level_height, TEXTURECACHE_TEXTURE_FORMAT);
|
|
||||||
|
|
||||||
// Transition image to transfer source, and invalidate the current state,
|
// Transition image to transfer source, and invalidate the current state,
|
||||||
// since we'll be executing the command buffer.
|
// since we'll be executing the command buffer.
|
||||||
|
@ -138,9 +132,9 @@ bool VKTexture::Save(const std::string& filename, unsigned int level)
|
||||||
StateTracker::GetInstance()->EndRenderPass();
|
StateTracker::GetInstance()->EndRenderPass();
|
||||||
|
|
||||||
// Copy to download buffer.
|
// Copy to download buffer.
|
||||||
staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
m_staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
|
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, x, y, width,
|
||||||
level_width, level_height, level, 0);
|
height, level, 0);
|
||||||
|
|
||||||
// Restore original state of texture.
|
// Restore original state of texture.
|
||||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
@ -150,21 +144,23 @@ bool VKTexture::Save(const std::string& filename, unsigned int level)
|
||||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||||
|
|
||||||
// Map the staging texture so we can copy the contents out.
|
// Map the staging texture so we can copy the contents out.
|
||||||
if (!staging_texture->Map())
|
if (!m_staging_texture->Map())
|
||||||
{
|
{
|
||||||
PanicAlert("Failed to map staging texture");
|
PanicAlert("Failed to map staging texture");
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write texture out to file.
|
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_texture->GetMapPointer()),
|
||||||
// It's okay to throw this texture away immediately, since we're done with it, and
|
static_cast<u32>(m_staging_texture->GetRowStride()), width,
|
||||||
// we blocked until the copy completed on the GPU anyway.
|
height};
|
||||||
bool result = TextureToPng(reinterpret_cast<u8*>(staging_texture->GetMapPointer()),
|
}
|
||||||
static_cast<u32>(staging_texture->GetRowStride()), filename,
|
|
||||||
level_width, level_height);
|
|
||||||
|
|
||||||
staging_texture->Unmap();
|
void VKTexture::Unmap()
|
||||||
return result;
|
{
|
||||||
|
if (!m_staging_texture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_staging_texture->Unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
~VKTexture();
|
~VKTexture();
|
||||||
|
|
||||||
void Bind(unsigned int stage) override;
|
void Bind(unsigned int stage) override;
|
||||||
bool Save(const std::string& filename, unsigned int level) override;
|
void Unmap() override;
|
||||||
|
|
||||||
void CopyRectangleFromTexture(const AbstractTexture* source,
|
void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||||
const MathUtil::Rectangle<int>& srcrect,
|
const MathUtil::Rectangle<int>& srcrect,
|
||||||
|
@ -47,7 +47,12 @@ private:
|
||||||
void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
|
void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
|
||||||
const MathUtil::Rectangle<int>& src_rect);
|
const MathUtil::Rectangle<int>& src_rect);
|
||||||
|
|
||||||
|
std::optional<RawTextureInfo> MapFullImpl() override;
|
||||||
|
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||||
|
u32 height) override;
|
||||||
|
|
||||||
std::unique_ptr<Texture2D> m_texture;
|
std::unique_ptr<Texture2D> m_texture;
|
||||||
|
std::unique_ptr<StagingTexture2D> m_staging_texture;
|
||||||
VkFramebuffer m_framebuffer;
|
VkFramebuffer m_framebuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "VideoCommon/AbstractTexture.h"
|
#include "Common/Assert.h"
|
||||||
|
|
||||||
AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c)
|
#include "VideoCommon/AbstractTexture.h"
|
||||||
|
#include "VideoCommon/ImageWrite.h"
|
||||||
|
|
||||||
|
AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c), m_currently_mapped(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +17,87 @@ AbstractTexture::~AbstractTexture() = default;
|
||||||
|
|
||||||
bool AbstractTexture::Save(const std::string& filename, unsigned int level)
|
bool AbstractTexture::Save(const std::string& filename, unsigned int level)
|
||||||
{
|
{
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
auto result = level == 0 ? Map() : Map(level);
|
||||||
|
|
||||||
|
if (!result.has_value())
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto raw_data = result.value();
|
||||||
|
return TextureToPng(raw_data.data, raw_data.stride, filename, raw_data.width, raw_data.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map()
|
||||||
|
{
|
||||||
|
if (m_currently_mapped)
|
||||||
|
{
|
||||||
|
Unmap();
|
||||||
|
m_currently_mapped = false;
|
||||||
|
}
|
||||||
|
auto result = MapFullImpl();
|
||||||
|
|
||||||
|
if (!result.has_value())
|
||||||
|
{
|
||||||
|
m_currently_mapped = false;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currently_mapped = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level, u32 x, u32 y,
|
||||||
|
u32 width, u32 height)
|
||||||
|
{
|
||||||
|
_assert_(level < m_config.levels);
|
||||||
|
|
||||||
|
u32 max_level_width = std::max(m_config.width >> level, 1u);
|
||||||
|
u32 max_level_height = std::max(m_config.height >> level, 1u);
|
||||||
|
|
||||||
|
_assert_(width < max_level_width);
|
||||||
|
_assert_(height < max_level_height);
|
||||||
|
|
||||||
|
auto result = MapRegionImpl(level, x, y, width, height);
|
||||||
|
|
||||||
|
if (!result.has_value())
|
||||||
|
{
|
||||||
|
m_currently_mapped = false;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currently_mapped = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level)
|
||||||
|
{
|
||||||
|
_assert_(level < m_config.levels);
|
||||||
|
|
||||||
|
u32 level_width = std::max(m_config.width >> level, 1u);
|
||||||
|
u32 level_height = std::max(m_config.height >> level, 1u);
|
||||||
|
|
||||||
|
return Map(level, 0, 0, level_width, level_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractTexture::Unmap()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::MapFullImpl()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AbstractTexture::RawTextureInfo>
|
||||||
|
AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format)
|
bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -17,7 +18,20 @@ public:
|
||||||
explicit AbstractTexture(const TextureConfig& c);
|
explicit AbstractTexture(const TextureConfig& c);
|
||||||
virtual ~AbstractTexture();
|
virtual ~AbstractTexture();
|
||||||
virtual void Bind(unsigned int stage) = 0;
|
virtual void Bind(unsigned int stage) = 0;
|
||||||
virtual bool Save(const std::string& filename, unsigned int level);
|
bool Save(const std::string& filename, unsigned int level);
|
||||||
|
|
||||||
|
struct RawTextureInfo
|
||||||
|
{
|
||||||
|
const u8* data;
|
||||||
|
u32 stride;
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<RawTextureInfo> Map();
|
||||||
|
std::optional<RawTextureInfo> Map(u32 level, u32 x, u32 y, u32 width, u32 height);
|
||||||
|
std::optional<RawTextureInfo> Map(u32 level);
|
||||||
|
virtual void Unmap();
|
||||||
|
|
||||||
virtual void CopyRectangleFromTexture(const AbstractTexture* source,
|
virtual void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||||
const MathUtil::Rectangle<int>& srcrect,
|
const MathUtil::Rectangle<int>& srcrect,
|
||||||
|
@ -31,5 +45,10 @@ public:
|
||||||
const TextureConfig& GetConfig() const;
|
const TextureConfig& GetConfig() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual std::optional<RawTextureInfo> MapFullImpl();
|
||||||
|
virtual std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||||
|
u32 height);
|
||||||
|
bool m_currently_mapped = false;
|
||||||
|
|
||||||
const TextureConfig m_config;
|
const TextureConfig m_config;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
|
|
||||||
|
#include "VideoCommon/AbstractTexture.h"
|
||||||
#include "VideoCommon/AVIDump.h"
|
#include "VideoCommon/AVIDump.h"
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
#include "VideoCommon/CPMemory.h"
|
#include "VideoCommon/CPMemory.h"
|
||||||
|
@ -645,6 +646,20 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
||||||
m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
|
m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The FinishFrameData 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.
|
||||||
|
FinishFrameData();
|
||||||
|
if (IsFrameDumping())
|
||||||
|
{
|
||||||
|
auto result = m_last_xfb_texture->Map();
|
||||||
|
if (result.has_value())
|
||||||
|
{
|
||||||
|
auto raw_data = result.value();
|
||||||
|
DumpFrameData(raw_data.data, raw_data.width, raw_data.height, raw_data.stride,
|
||||||
|
AVIDump::FetchState(ticks));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (xfbAddr && fbWidth && fbStride && fbHeight)
|
if (xfbAddr && fbWidth && fbStride && fbHeight)
|
||||||
{
|
{
|
||||||
constexpr int force_safe_texture_cache_hash = 0;
|
constexpr int force_safe_texture_cache_hash = 0;
|
||||||
|
@ -654,6 +669,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
||||||
|
|
||||||
// TODO, check if xfb_entry is a duplicate of the previous frame and skip SwapImpl
|
// TODO, check if xfb_entry is a duplicate of the previous frame and skip SwapImpl
|
||||||
|
|
||||||
|
m_previous_xfb_texture = xfb_entry->texture.get();
|
||||||
|
|
||||||
|
m_last_xfb_texture = xfb_entry->texture.get();
|
||||||
|
|
||||||
// TODO: merge more generic parts into VideoCommon
|
// TODO: merge more generic parts into VideoCommon
|
||||||
g_renderer->SwapImpl(xfb_entry->texture.get(), rc, ticks, Gamma);
|
g_renderer->SwapImpl(xfb_entry->texture.get(), rc, ticks, Gamma);
|
||||||
|
@ -697,12 +715,9 @@ void Renderer::ShutdownFrameDumping()
|
||||||
m_frame_dump_start.Set();
|
m_frame_dump_start.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state,
|
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state)
|
||||||
bool swap_upside_down)
|
|
||||||
{
|
{
|
||||||
FinishFrameData();
|
m_frame_dump_config = FrameDumpConfig{ m_last_xfb_texture, data, w, h, stride, state };
|
||||||
|
|
||||||
m_frame_dump_config = FrameDumpConfig{data, w, h, stride, swap_upside_down, state};
|
|
||||||
|
|
||||||
if (!m_frame_dump_thread_running.IsSet())
|
if (!m_frame_dump_thread_running.IsSet())
|
||||||
{
|
{
|
||||||
|
@ -723,6 +738,7 @@ void Renderer::FinishFrameData()
|
||||||
|
|
||||||
m_frame_dump_done.Wait();
|
m_frame_dump_done.Wait();
|
||||||
m_frame_dump_frame_running = false;
|
m_frame_dump_frame_running = false;
|
||||||
|
m_frame_dump_config.texture->Unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::RunFrameDumps()
|
void Renderer::RunFrameDumps()
|
||||||
|
@ -749,12 +765,6 @@ void Renderer::RunFrameDumps()
|
||||||
|
|
||||||
auto config = m_frame_dump_config;
|
auto config = m_frame_dump_config;
|
||||||
|
|
||||||
if (config.upside_down)
|
|
||||||
{
|
|
||||||
config.data = config.data + (config.height - 1) * config.stride;
|
|
||||||
config.stride = -config.stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save screenshot
|
// Save screenshot
|
||||||
if (m_screenshot_request.TestAndClear())
|
if (m_screenshot_request.TestAndClear())
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "VideoCommon/RenderState.h"
|
#include "VideoCommon/RenderState.h"
|
||||||
#include "VideoCommon/VideoCommon.h"
|
#include "VideoCommon/VideoCommon.h"
|
||||||
|
|
||||||
|
class AbstractRawTexture;
|
||||||
class AbstractTexture;
|
class AbstractTexture;
|
||||||
class PostProcessingShaderImplementation;
|
class PostProcessingShaderImplementation;
|
||||||
enum class EFBAccessType;
|
enum class EFBAccessType;
|
||||||
|
@ -152,11 +153,6 @@ protected:
|
||||||
void CheckFifoRecording();
|
void CheckFifoRecording();
|
||||||
void RecordVideoMemory();
|
void RecordVideoMemory();
|
||||||
|
|
||||||
bool IsFrameDumping();
|
|
||||||
void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state,
|
|
||||||
bool swap_upside_down = false);
|
|
||||||
void FinishFrameData();
|
|
||||||
|
|
||||||
Common::Flag m_screenshot_request;
|
Common::Flag m_screenshot_request;
|
||||||
Common::Event m_screenshot_completed;
|
Common::Event m_screenshot_completed;
|
||||||
std::mutex m_screenshot_lock;
|
std::mutex m_screenshot_lock;
|
||||||
|
@ -205,14 +201,16 @@ private:
|
||||||
bool m_frame_dump_frame_running = false;
|
bool m_frame_dump_frame_running = false;
|
||||||
struct FrameDumpConfig
|
struct FrameDumpConfig
|
||||||
{
|
{
|
||||||
|
AbstractTexture* texture;
|
||||||
const u8* data;
|
const u8* data;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int stride;
|
int stride;
|
||||||
bool upside_down;
|
|
||||||
AVIDump::Frame state;
|
AVIDump::Frame state;
|
||||||
} m_frame_dump_config;
|
} m_frame_dump_config;
|
||||||
|
|
||||||
|
AbstractTexture * m_last_xfb_texture;
|
||||||
|
|
||||||
// NOTE: The methods below are called on the framedumping thread.
|
// NOTE: The methods below are called on the framedumping thread.
|
||||||
bool StartFrameDumpToAVI(const FrameDumpConfig& config);
|
bool StartFrameDumpToAVI(const FrameDumpConfig& config);
|
||||||
void DumpFrameToAVI(const FrameDumpConfig& config);
|
void DumpFrameToAVI(const FrameDumpConfig& config);
|
||||||
|
@ -220,6 +218,10 @@ private:
|
||||||
std::string GetFrameDumpNextImageFileName() const;
|
std::string GetFrameDumpNextImageFileName() const;
|
||||||
bool StartFrameDumpToImage(const FrameDumpConfig& config);
|
bool StartFrameDumpToImage(const FrameDumpConfig& config);
|
||||||
void DumpFrameToImage(const FrameDumpConfig& config);
|
void DumpFrameToImage(const FrameDumpConfig& config);
|
||||||
|
|
||||||
|
bool IsFrameDumping();
|
||||||
|
void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state);
|
||||||
|
void FinishFrameData();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<Renderer> g_renderer;
|
extern std::unique_ptr<Renderer> g_renderer;
|
||||||
|
|
Loading…
Reference in New Issue