Merge pull request #4436 from stenzek/vulkan-full-ir-framedump

VideoBackends: Internal resolution frame dumping
This commit is contained in:
Stenzek 2016-11-28 22:05:16 +10:00 committed by GitHub
commit 0212741574
18 changed files with 357 additions and 131 deletions

View File

@ -236,6 +236,11 @@ static wxString cache_hires_textures_desc =
"more RAM but fixes possible stuttering.\n\nIf unsure, leave this unchecked.");
static wxString dump_efb_desc = wxTRANSLATE(
"Dump the contents of EFB copies to User/Dump/Textures/.\n\nIf unsure, leave this unchecked.");
static wxString internal_resolution_frame_dumping_desc = wxTRANSLATE(
"Create frame dumps and screenshots at the internal resolution of the renderer, rather than "
"the size of the window it is displayed within. If the aspect ratio is widescreen, the output "
"image will be scaled horizontally to preserve the vertical resolution.\n\nIf unsure, leave "
"this unchecked.");
#if defined(HAVE_LIBAV) || defined(_WIN32)
static wxString use_ffv1_desc =
wxTRANSLATE("Encode frame dumps using the FFV1 codec.\n\nIf unsure, leave this unchecked.");
@ -858,6 +863,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title)
CreateCheckBox(page_advanced, _("Prefetch Custom Textures"),
wxGetTranslation(cache_hires_textures_desc), vconfig.bCacheHiresTextures);
szr_utility->Add(cache_hires_textures);
if (vconfig.backend_info.bSupportsInternalResolutionFrameDumps)
{
szr_utility->Add(CreateCheckBox(page_advanced, _("Full Resolution Frame Dumps"),
wxGetTranslation(internal_resolution_frame_dumping_desc),
vconfig.bInternalResolutionFrameDumps));
}
szr_utility->Add(CreateCheckBox(page_advanced, _("Dump EFB Target"),
wxGetTranslation(dump_efb_desc), vconfig.bDumpEFBTarget));
szr_utility->Add(CreateCheckBox(page_advanced, _("Free Look"),

View File

@ -243,13 +243,13 @@ Renderer::Renderer(void*& window_handle)
FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
s_last_multisamples = g_ActiveConfig.iMultisamples;
s_last_efb_scale = g_ActiveConfig.iEFBScale;
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
CalculateTargetSize(s_backbuffer_width, s_backbuffer_height);
CalculateTargetSize();
PixelShaderManager::SetEfbScaleChanged();
SetupDeviceObjects();
@ -732,7 +732,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
ResetAPIState();
// Prepare to copy the XFBs to our backbuffer
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
TargetRectangle targetRc = GetTargetRectangle();
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
@ -867,7 +867,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
D3D::Present();
// Resize the back buffers NOW to avoid flickering
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || xfbchanged || windowResized ||
if (CalculateTargetSize() || xfbchanged || windowResized ||
s_last_efb_scale != g_ActiveConfig.iEFBScale ||
s_last_multisamples != g_ActiveConfig.iMultisamples ||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
@ -886,7 +886,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
s_backbuffer_height = D3D::GetBackBufferHeight();
}
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
s_last_efb_scale = g_ActiveConfig.iEFBScale;
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;

View File

@ -72,6 +72,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsDepthClamp = true;
g_Config.backend_info.bSupportsReversedDepthRange = false;
g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
IDXGIFactory* factory;
IDXGIAdapter* ad;

View File

@ -222,13 +222,13 @@ Renderer::Renderer(void*& window_handle)
FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
s_last_multisamples = g_ActiveConfig.iMultisamples;
s_last_efb_scale = g_ActiveConfig.iEFBScale;
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
CalculateTargetSize(s_backbuffer_width, s_backbuffer_height);
CalculateTargetSize();
PixelShaderManager::SetEfbScaleChanged();
SetupDeviceObjects();
@ -654,7 +654,7 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
BBox::Invalidate();
// Prepare to copy the XFBs to our backbuffer
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
TargetRectangle target_rc = GetTargetRectangle();
D3D::GetBackBuffer()->TransitionToResourceState(D3D::current_command_list,
@ -819,8 +819,8 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
D3D::Present();
// Resize the back buffers NOW to avoid flickering
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || xfb_changed ||
window_resized || s_last_efb_scale != g_ActiveConfig.iEFBScale ||
if (CalculateTargetSize() || xfb_changed || window_resized ||
s_last_efb_scale != g_ActiveConfig.iEFBScale ||
s_last_multisamples != g_ActiveConfig.iMultisamples ||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
{
@ -851,7 +851,7 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
s_backbuffer_height = D3D::GetBackBufferHeight();
}
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
s_last_efb_scale = g_ActiveConfig.iEFBScale;
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;

View File

@ -75,6 +75,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsDepthClamp = true;
g_Config.backend_info.bSupportsReversedDepthRange = false;
g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
IDXGIFactory* factory;
IDXGIAdapter* ad;

View File

@ -41,6 +41,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsDepthClamp = true;
g_Config.backend_info.bSupportsReversedDepthRange = true;
g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
// aamodes: We only support 1 sample, so no MSAA
g_Config.backend_info.Adapters.clear();

View File

@ -44,8 +44,6 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
{
ApplyShader();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(dst.left, dst.bottom, dst.GetWidth(), dst.GetHeight());
OpenGL_BindAttributelessVAO();

View File

@ -700,10 +700,10 @@ Renderer::Renderer()
FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
s_last_efb_scale = g_ActiveConfig.iEFBScale;
CalculateTargetSize(s_backbuffer_width, s_backbuffer_height);
CalculateTargetSize();
PixelShaderManager::SetEfbScaleChanged();
@ -768,8 +768,7 @@ Renderer::~Renderer()
{
FlushFrameDump();
FinishFrameData();
if (m_frame_dumping_pbo[0])
glDeleteBuffers(2, m_frame_dumping_pbo.data());
DestroyFrameDumpResources();
}
void Renderer::Shutdown()
@ -1373,84 +1372,37 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
ResetAPIState();
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
TargetRectangle flipped_trc = GetTargetRectangle();
// Flip top and bottom for some reason; TODO: Fix the code to suck less?
std::swap(flipped_trc.top, flipped_trc.bottom);
// Copy the framebuffer to screen.
const XFBSource* xfbSource = nullptr;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
DrawFrame(flipped_trc, rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight);
if (g_ActiveConfig.bUseXFB)
// 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();
if (IsFrameDumping())
{
// draw each xfb source
for (u32 i = 0; i < xfbCount; ++i)
// Currently, we only use the off-screen buffer as a frame dump source if full-resolution
// frame dumping is enabled, saving the need for an extra copy. In the future, this could
// be extended to be used for surfaceless contexts as well.
bool use_offscreen_buffer = g_ActiveConfig.bInternalResolutionFrameDumps;
if (use_offscreen_buffer)
{
xfbSource = (const XFBSource*)xfbSourceList[i];
TargetRectangle drawRc;
TargetRectangle sourceRc;
sourceRc.left = xfbSource->sourceRc.left;
sourceRc.right = xfbSource->sourceRc.right;
sourceRc.top = xfbSource->sourceRc.top;
sourceRc.bottom = xfbSource->sourceRc.bottom;
if (g_ActiveConfig.bUseRealXFB)
{
drawRc = flipped_trc;
sourceRc.right -= fbStride - fbWidth;
// RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a
// top-left origin.
// The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
sourceRc.top = sourceRc.bottom;
sourceRc.bottom = 0;
}
else
{
// use virtual xfb with offset
int xfbHeight = xfbSource->srcHeight;
int xfbWidth = xfbSource->srcWidth;
int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbStride * 2);
drawRc.top = flipped_trc.top - hOffset * flipped_trc.GetHeight() / (s32)fbHeight;
drawRc.bottom =
flipped_trc.top - (hOffset + xfbHeight) * flipped_trc.GetHeight() / (s32)fbHeight;
drawRc.left =
flipped_trc.left +
(flipped_trc.GetWidth() - xfbWidth * flipped_trc.GetWidth() / (s32)fbStride) / 2;
drawRc.right =
flipped_trc.left +
(flipped_trc.GetWidth() + xfbWidth * flipped_trc.GetWidth() / (s32)fbStride) / 2;
// The following code disables auto stretch. Kept for reference.
// scale draw area for a 1 to 1 pixel mapping with the draw target
// float vScale = (float)fbHeight / (float)flipped_trc.GetHeight();
// float hScale = (float)fbWidth / (float)flipped_trc.GetWidth();
// drawRc.top *= vScale;
// drawRc.bottom *= vScale;
// drawRc.left *= hScale;
// drawRc.right *= hScale;
sourceRc.right -= Renderer::EFBToScaledX(fbStride - fbWidth);
}
BlitScreen(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
// DumpFrameUsingFBO resets GL_FRAMEBUFFER, so change back to the window for drawing OSD.
DumpFrameUsingFBO(rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight, ticks);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
else
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
DumpFrame(flipped_trc, ticks);
}
}
else
{
TargetRectangle targetRc = ConvertEFBRectangle(rc);
// for msaa mode, we must resolve the efb content to non-msaa
GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
BlitScreen(targetRc, flipped_trc, tex, s_target_width, s_target_height);
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
DumpFrame(flipped_trc, ticks);
// Finish up the current frame, print some stats
@ -1484,7 +1436,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
s_last_efb_scale = g_ActiveConfig.iEFBScale;
}
bool TargetSizeChanged = false;
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height))
if (CalculateTargetSize())
{
TargetSizeChanged = true;
}
@ -1494,7 +1446,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
{
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
if (TargetSizeChanged || s_last_multisamples != g_ActiveConfig.iMultisamples ||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
@ -1580,6 +1532,104 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
ClearEFBCache();
}
void Renderer::DrawFrame(const TargetRectangle& target_rc, const EFBRectangle& source_rc,
u32 xfb_addr, const XFBSourceBase* const* xfb_sources, u32 xfb_count,
u32 fb_width, u32 fb_stride, u32 fb_height)
{
if (g_ActiveConfig.bUseXFB)
{
if (g_ActiveConfig.bUseRealXFB)
DrawRealXFB(target_rc, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
else
DrawVirtualXFB(target_rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
}
else
{
DrawEFB(target_rc, source_rc);
}
}
void Renderer::DrawEFB(const TargetRectangle& target_rc, const EFBRectangle& source_rc)
{
TargetRectangle scaled_source_rc = ConvertEFBRectangle(source_rc);
// for msaa mode, we must resolve the efb content to non-msaa
GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc);
BlitScreen(scaled_source_rc, target_rc, tex, s_target_width, s_target_height);
}
void Renderer::DrawVirtualXFB(const TargetRectangle& target_rc, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height)
{
for (u32 i = 0; i < xfb_count; ++i)
{
const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
TargetRectangle draw_rc;
TargetRectangle source_rc;
source_rc.left = xfbSource->sourceRc.left;
source_rc.right = xfbSource->sourceRc.right;
source_rc.top = xfbSource->sourceRc.top;
source_rc.bottom = xfbSource->sourceRc.bottom;
// use virtual xfb with offset
int xfbHeight = xfbSource->srcHeight;
int xfbWidth = xfbSource->srcWidth;
int hOffset = (static_cast<s32>(xfbSource->srcAddr) - static_cast<s32>(xfb_addr)) /
(static_cast<s32>(fb_stride) * 2);
draw_rc.top = target_rc.top - hOffset * target_rc.GetHeight() / static_cast<s32>(fb_height);
draw_rc.bottom =
target_rc.top - (hOffset + xfbHeight) * target_rc.GetHeight() / static_cast<s32>(fb_height);
draw_rc.left =
target_rc.left +
(target_rc.GetWidth() - xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
draw_rc.right =
target_rc.left +
(target_rc.GetWidth() + xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
// The following code disables auto stretch. Kept for reference.
// scale draw area for a 1 to 1 pixel mapping with the draw target
// float h_scale = static_cast<float>(fb_width) / static_cast<float>(target_rc.GetWidth());
// float v_scale = static_cast<float>(fb_height) / static_cast<float>(target_rc.GetHeight());
// draw_rc.top *= v_scale;
// draw_rc.bottom *= v_scale;
// draw_rc.left *= h_scale;
// draw_rc.right *= h_scale;
source_rc.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
}
}
void Renderer::DrawRealXFB(const TargetRectangle& target_rc,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height)
{
for (u32 i = 0; i < xfb_count; ++i)
{
const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
TargetRectangle source_rc;
source_rc.left = xfbSource->sourceRc.left;
source_rc.right = xfbSource->sourceRc.right;
source_rc.top = xfbSource->sourceRc.top;
source_rc.bottom = xfbSource->sourceRc.bottom;
source_rc.right -= fb_stride - fb_width;
// RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a top-
// left origin. The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
source_rc.top = source_rc.bottom;
source_rc.bottom = 0;
TargetRectangle draw_rc = target_rc;
BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
}
}
void Renderer::FlushFrameDump()
{
if (!m_last_frame_exported)
@ -1598,9 +1648,6 @@ void Renderer::FlushFrameDump()
void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
{
if (!IsFrameDumping())
return;
if (!m_frame_dumping_pbo[0])
{
glGenBuffers(2, m_frame_dumping_pbo.data());
@ -1637,6 +1684,82 @@ void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
void Renderer::DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count,
u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
{
// This needs to be converted to the GL bottom-up window coordinate system.
TargetRectangle render_rc = CalculateFrameDumpDrawRectangle();
std::swap(render_rc.top, render_rc.bottom);
// Ensure the render texture meets the size requirements of the draw area.
u32 render_width = static_cast<u32>(render_rc.GetWidth());
u32 render_height = static_cast<u32>(render_rc.GetHeight());
PrepareFrameDumpRenderTexture(render_width, render_height);
// Ensure the alpha channel of the render texture is blank. The frame dump backend expects
// that the alpha is set to 1.0 for all pixels.
FramebufferManager::SetFramebuffer(m_frame_dump_render_framebuffer);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Render the frame into the frame dump render texture. Disable alpha writes in case the
// post-processing shader writes a non-1.0 value.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
DrawFrame(render_rc, source_rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
// Copy frame to output buffer. This assumes that GL_FRAMEBUFFER has been set.
DumpFrame(render_rc, ticks);
// Restore state after drawing. This isn't the game state, it's the state set by ResetAPIState.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
FramebufferManager::SetFramebuffer(0);
}
void Renderer::PrepareFrameDumpRenderTexture(u32 width, u32 height)
{
// Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used).
// Or, resize texture if it isn't large enough to accommodate the current frame.
if (m_frame_dump_render_texture != 0 && m_frame_dump_render_framebuffer != 0 &&
m_frame_dump_render_texture_width >= width && m_frame_dump_render_texture_height >= height)
{
return;
}
// Recreate texture objects.
if (m_frame_dump_render_texture != 0)
glDeleteTextures(1, &m_frame_dump_render_texture);
if (m_frame_dump_render_framebuffer != 0)
glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
glGenTextures(1, &m_frame_dump_render_texture);
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D, m_frame_dump_render_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glGenFramebuffers(1, &m_frame_dump_render_framebuffer);
FramebufferManager::SetFramebuffer(m_frame_dump_render_framebuffer);
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
m_frame_dump_render_texture, 0);
m_frame_dump_render_texture_width = width;
m_frame_dump_render_texture_height = height;
TextureCache::SetStage();
}
void Renderer::DestroyFrameDumpResources()
{
if (m_frame_dump_render_framebuffer)
glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
if (m_frame_dump_render_texture)
glDeleteTextures(1, &m_frame_dump_render_texture);
if (m_frame_dumping_pbo[0])
glDeleteBuffers(2, m_frame_dumping_pbo.data());
}
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
void Renderer::ResetAPIState()
{

View File

@ -8,6 +8,8 @@
#include <string>
#include "VideoCommon/RenderBase.h"
struct XFBSourceBase;
namespace OGL
{
void ClearEFBCache();
@ -110,11 +112,33 @@ private:
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
const TargetRectangle& targetPixelRc, const void* data);
// Draw either the EFB, or specified XFB sources to the currently-bound framebuffer.
void DrawFrame(const TargetRectangle& target_rc, const EFBRectangle& source_rc, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height);
void DrawEFB(const TargetRectangle& target_rc, const EFBRectangle& source_rc);
void DrawVirtualXFB(const TargetRectangle& target_rc, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height);
void DrawRealXFB(const TargetRectangle& target_rc, const XFBSourceBase* const* xfb_sources,
u32 xfb_count, u32 fb_width, u32 fb_stride, u32 fb_height);
void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width,
int src_height);
void FlushFrameDump();
void DumpFrame(const TargetRectangle& flipped_trc, u64 ticks);
void DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height, u64 ticks);
// Frame dumping framebuffer, we render to this, then read it back
void PrepareFrameDumpRenderTexture(u32 width, u32 height);
void DestroyFrameDumpResources();
GLuint m_frame_dump_render_texture = 0;
GLuint m_frame_dump_render_framebuffer = 0;
u32 m_frame_dump_render_texture_width = 0;
u32 m_frame_dump_render_texture_height = 0;
// avi dumping state to delay one frame
std::array<u32, 2> m_frame_dumping_pbo = {};

View File

@ -104,6 +104,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsSSAA = true;
g_Config.backend_info.bSupportsReversedDepthRange = true;
g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = true;
// Overwritten in Render.cpp later
g_Config.backend_info.bSupportsDualSourceBlend = true;

View File

@ -132,6 +132,7 @@ void VideoSoftware::InitBackendInfo()
g_Config.backend_info.bSupportsOversizedViewports = true;
g_Config.backend_info.bSupportsPrimitiveRestart = false;
g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
// aamodes
g_Config.backend_info.AAModes = {1};

View File

@ -54,8 +54,8 @@ Renderer::Renderer(std::unique_ptr<SwapChain> swap_chain) : m_swap_chain(std::mo
s_backbuffer_width = m_swap_chain ? m_swap_chain->GetWidth() : MAX_XFB_WIDTH;
s_backbuffer_height = m_swap_chain ? m_swap_chain->GetHeight() : MAX_XFB_HEIGHT;
s_last_efb_scale = g_ActiveConfig.iEFBScale;
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
CalculateTargetSize(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
CalculateTargetSize();
PixelShaderManager::SetEfbScaleChanged();
}
@ -711,13 +711,7 @@ bool Renderer::DrawFrameDump(const EFBRectangle& source_rect, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height, u64 ticks)
{
// Draw the screenshot to an image containing only the active screen area, removing any
// borders as a result of the game rendering in a different aspect ratio.
TargetRectangle target_rect = GetTargetRectangle();
target_rect.right = target_rect.GetWidth();
target_rect.bottom = target_rect.GetHeight();
target_rect.left = 0;
target_rect.top = 0;
TargetRectangle target_rect = CalculateFrameDumpDrawRectangle();
u32 width = std::max(1u, static_cast<u32>(target_rect.GetWidth()));
u32 height = std::max(1u, static_cast<u32>(target_rect.GetHeight()));
if (!ResizeFrameDumpBuffer(width, height))
@ -998,8 +992,8 @@ void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height)
FramebufferManagerBase::SetLastXfbHeight(new_height);
// Changing the XFB source area will likely change the final drawing rectangle.
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height))
UpdateDrawRectangle();
if (CalculateTargetSize())
{
PixelShaderManager::SetEfbScaleChanged();
ResizeEFBTextures();
@ -1112,11 +1106,11 @@ void Renderer::CheckForConfigChanges()
// If the aspect ratio is changed, this changes the area that the game is drawn to.
if (aspect_changed)
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
UpdateDrawRectangle();
if (efb_scale_changed || aspect_changed)
{
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height))
if (CalculateTargetSize())
ResizeEFBTextures();
}
@ -1157,8 +1151,8 @@ void Renderer::OnSwapChainResized()
{
s_backbuffer_width = m_swap_chain->GetWidth();
s_backbuffer_height = m_swap_chain->GetHeight();
UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height))
UpdateDrawRectangle();
if (CalculateTargetSize())
{
PixelShaderManager::SetEfbScaleChanged();
ResizeEFBTextures();

View File

@ -233,14 +233,15 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config)
config->backend_info.bSupportsPaletteConversion = true; // Assumed support.
config->backend_info.bSupportsClipControl = true; // Assumed support.
config->backend_info.bSupportsMultithreading = true; // Assumed support.
config->backend_info.bSupportsPostProcessing = false; // No support yet.
config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features.
config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
config->backend_info.bSupportsGSInstancing = false; // Dependent on features.
config->backend_info.bSupportsBBox = false; // Dependent on features.
config->backend_info.bSupportsSSAA = false; // Dependent on features.
config->backend_info.bSupportsDepthClamp = false; // Dependent on features.
config->backend_info.bSupportsReversedDepthRange = false; // No support yet due to driver bugs.
config->backend_info.bSupportsInternalResolutionFrameDumps = true; // Assumed support.
config->backend_info.bSupportsPostProcessing = false; // No support yet.
config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features.
config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
config->backend_info.bSupportsGSInstancing = false; // Dependent on features.
config->backend_info.bSupportsBBox = false; // Dependent on features.
config->backend_info.bSupportsSSAA = false; // Dependent on features.
config->backend_info.bSupportsDepthClamp = false; // Dependent on features.
config->backend_info.bSupportsReversedDepthRange = false; // No support yet due to driver bugs.
}
void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list)

View File

@ -60,6 +60,8 @@ public:
static int ScaleToVirtualXfbHeight(int y);
static unsigned int GetEFBLayers() { return m_EFBLayers; }
virtual void GetTargetSize(unsigned int* width, unsigned int* height) = 0;
protected:
struct VirtualXFB
{
@ -79,8 +81,6 @@ protected:
private:
virtual std::unique_ptr<XFBSourceBase>
CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) = 0;
// TODO: figure out why OGL is different for this guy
virtual void GetTargetSize(unsigned int* width, unsigned int* height) = 0;
static VirtualXFBListType::iterator FindVirtualXFB(u32 xfbAddr, u32 width, u32 height);

View File

@ -18,6 +18,7 @@
#include <mutex>
#include <string>
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/FileUtil.h"
@ -188,7 +189,7 @@ void Renderer::CalculateTargetScale(int x, int y, int* scaledX, int* scaledY)
}
// return true if target size changed
bool Renderer::CalculateTargetSize(unsigned int framebuffer_width, unsigned int framebuffer_height)
bool Renderer::CalculateTargetSize()
{
int newEFBWidth, newEFBHeight;
newEFBWidth = newEFBHeight = 0;
@ -449,10 +450,80 @@ void Renderer::DrawDebugText()
g_renderer->RenderText(final_yellow, 20, 20, 0xFFFFFF00);
}
void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
float Renderer::CalculateDrawAspectRatio(int target_width, int target_height)
{
float FloatGLWidth = (float)backbuffer_width;
float FloatGLHeight = (float)backbuffer_height;
// The dimensions are the sizes that are used to create the EFB/backbuffer textures, so
// they should always be greater than zero.
_assert_(target_width > 0 && target_height > 0);
if (g_ActiveConfig.iAspectRatio == ASPECT_STRETCH)
{
// If stretch is enabled, we prefer the aspect ratio of the window.
return (static_cast<float>(target_width) / static_cast<float>(target_height)) /
(static_cast<float>(s_backbuffer_width) / static_cast<float>(s_backbuffer_height));
}
// The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio
if (g_ActiveConfig.iAspectRatio == ASPECT_ANALOG_WIDE ||
(g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && Core::g_aspect_wide))
{
return (static_cast<float>(target_width) / static_cast<float>(target_height)) /
AspectToWidescreen(VideoInterface::GetAspectRatio());
}
else
{
return (static_cast<float>(target_width) / static_cast<float>(target_height)) /
VideoInterface::GetAspectRatio();
}
}
TargetRectangle Renderer::CalculateFrameDumpDrawRectangle()
{
// No point including any borders in the frame dump image, since they'd have to be cropped anyway.
TargetRectangle rc;
rc.left = 0;
rc.top = 0;
// If full-resolution frame dumping is disabled, just use the window draw rectangle.
// Also do this if RealXFB is enabled, since the image has been downscaled for the XFB copy
// anyway, and there's no point writing an upscaled frame with no filtering.
if (!g_ActiveConfig.bInternalResolutionFrameDumps || g_ActiveConfig.RealXFBEnabled())
{
// But still remove the borders, since the caller expects this.
rc.right = target_rc.GetWidth();
rc.bottom = target_rc.GetHeight();
return rc;
}
// Grab the dimensions of the EFB textures, we scale either of these depending on the ratio.
unsigned int efb_width, efb_height;
g_framebuffer_manager->GetTargetSize(&efb_width, &efb_height);
// Scale either the width or height depending the content aspect ratio.
// This way we preserve as much resolution as possible when scaling.
float ratio = CalculateDrawAspectRatio(efb_width, efb_height);
float draw_width, draw_height;
if (ratio >= 1.0f)
{
// Preserve horizontal resolution, scale vertically.
draw_width = static_cast<float>(efb_width);
draw_height = static_cast<float>(efb_height) * ratio;
}
else
{
// Preserve vertical resolution, scale horizontally.
draw_width = static_cast<float>(efb_width) / ratio;
draw_height = static_cast<float>(efb_height);
}
rc.right = static_cast<int>(std::ceil(draw_width));
rc.bottom = static_cast<int>(std::ceil(draw_height));
return rc;
}
void Renderer::UpdateDrawRectangle()
{
float FloatGLWidth = static_cast<float>(s_backbuffer_width);
float FloatGLHeight = static_cast<float>(s_backbuffer_height);
float FloatXOffset = 0;
float FloatYOffset = 0;
@ -511,17 +582,7 @@ void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
// Check for force-settings and override.
// The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio
float Ratio;
if (g_ActiveConfig.iAspectRatio == ASPECT_ANALOG_WIDE ||
(g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && Core::g_aspect_wide))
{
Ratio = (WinWidth / WinHeight) / AspectToWidescreen(VideoInterface::GetAspectRatio());
}
else
{
Ratio = (WinWidth / WinHeight) / VideoInterface::GetAspectRatio();
}
float Ratio = CalculateDrawAspectRatio(s_backbuffer_width, s_backbuffer_height);
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH)
{
if (Ratio > 1.0f)

View File

@ -93,7 +93,9 @@ public:
virtual TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) = 0;
static const TargetRectangle& GetTargetRectangle() { return target_rc; }
static void UpdateDrawRectangle(int backbuffer_width, int backbuffer_height);
static float CalculateDrawAspectRatio(int target_width, int target_height);
static TargetRectangle CalculateFrameDumpDrawRectangle();
static void UpdateDrawRectangle();
// Use this to convert a single target rectangle to two stereo rectangles
static void ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc,
@ -143,7 +145,7 @@ public:
virtual void ChangeSurface(void* new_surface_handle) {}
protected:
static void CalculateTargetScale(int x, int y, int* scaledX, int* scaledY);
bool CalculateTargetSize(unsigned int framebuffer_width, unsigned int framebuffer_height);
bool CalculateTargetSize();
static void CheckFifoRecording();
static void RecordVideoMemory();

View File

@ -39,6 +39,7 @@ VideoConfig::VideoConfig()
backend_info.api_type = APIType::Nothing;
backend_info.bSupportsExclusiveFullscreen = false;
backend_info.bSupportsMultithreading = false;
backend_info.bSupportsInternalResolutionFrameDumps = false;
bEnableValidationLayer = false;
bBackendMultithreading = true;
@ -74,6 +75,7 @@ void VideoConfig::Load(const std::string& ini_file)
settings->Get("DumpFramesAsImages", &bDumpFramesAsImages, 0);
settings->Get("FreeLook", &bFreeLook, 0);
settings->Get("UseFFV1", &bUseFFV1, 0);
settings->Get("InternalResolutionFrameDumps", &bInternalResolutionFrameDumps, 0);
settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
settings->Get("FastDepthCalc", &bFastDepthCalc, true);
settings->Get("MSAA", &iMultisamples, 1);
@ -291,6 +293,7 @@ void VideoConfig::Save(const std::string& ini_file)
settings->Set("DumpFramesAsImages", bDumpFramesAsImages);
settings->Set("FreeLook", bFreeLook);
settings->Set("UseFFV1", bUseFFV1);
settings->Set("InternalResolutionFrameDumps", bInternalResolutionFrameDumps);
settings->Set("EnablePixelLighting", bEnablePixelLighting);
settings->Set("FastDepthCalc", bFastDepthCalc);
settings->Set("MSAA", iMultisamples);

View File

@ -101,6 +101,7 @@ struct VideoConfig final
bool bDumpEFBTarget;
bool bDumpFramesAsImages;
bool bUseFFV1;
bool bInternalResolutionFrameDumps;
bool bFreeLook;
bool bBorderlessFullscreen;
@ -184,6 +185,7 @@ struct VideoConfig final
bool bSupportsDepthClamp; // Needed by VertexShaderGen, so must stay in VideoCommon
bool bSupportsReversedDepthRange;
bool bSupportsMultithreading;
bool bSupportsInternalResolutionFrameDumps;
} backend_info;
// Utility