Move most backend functionality to VideoCommon

This commit is contained in:
Stenzek
2019-02-15 11:59:50 +10:00
parent 933f3ba008
commit f039149198
182 changed files with 8334 additions and 15917 deletions

View File

@ -50,14 +50,15 @@
#include "VideoCommon/AbstractFramebuffer.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/Debugger.h"
#include "VideoCommon/FPSCounter.h"
#include "VideoCommon/FramebufferManagerBase.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/ShaderCache.h"
@ -68,12 +69,10 @@
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VertexManagerBase.h"
#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/XFMemory.h"
// TODO: Move these out of here.
int frameCount;
std::unique_ptr<Renderer> g_renderer;
static float AspectToWidescreen(float aspect)
@ -97,7 +96,14 @@ Renderer::~Renderer() = default;
bool Renderer::Initialize()
{
return InitializeImGui();
if (!InitializeImGui())
return false;
m_post_processor = std::make_unique<VideoCommon::PostProcessing>();
if (!m_post_processor->Initialize(m_backbuffer_format))
return false;
return true;
}
void Renderer::Shutdown()
@ -106,6 +112,142 @@ void Renderer::Shutdown()
// can require additional graphics sub-systems so it needs to be done first
ShutdownFrameDumping();
ShutdownImGui();
m_post_processor.reset();
}
void Renderer::BeginUtilityDrawing()
{
g_vertex_manager->Flush();
}
void Renderer::EndUtilityDrawing()
{
// Reset framebuffer/scissor/viewport. Pipeline will be reset at next draw.
g_framebuffer_manager->BindEFBFramebuffer();
BPFunctions::SetScissor();
BPFunctions::SetViewport();
}
void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer)
{
m_current_framebuffer = framebuffer;
}
void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
{
m_current_framebuffer = framebuffer;
}
void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
const ClearColor& color_value, float depth_value)
{
m_current_framebuffer = framebuffer;
}
std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
const std::string& source)
{
return CreateShaderFromSource(stage, source.c_str(), source.size());
}
void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z)
{
g_framebuffer_manager->ClearEFB(rc, colorEnable, alphaEnable, zEnable, color, z);
}
void Renderer::ReinterpretPixelData(EFBReinterpretType convtype)
{
g_framebuffer_manager->ReinterpretPixelData(convtype);
}
u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
{
if (type == EFBAccessType::PeekColor)
{
u32 color = g_framebuffer_manager->PeekEFBColor(x, y);
// a little-endian value is expected to be returned
color = ((color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000));
// check what to do with the alpha channel (GX_PokeAlphaRead)
PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
if (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24)
{
color = RGBA8ToRGBA6ToRGBA8(color);
}
else if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
{
color = RGBA8ToRGB565ToRGBA8(color);
}
if (bpmem.zcontrol.pixel_format != PEControl::RGBA6_Z24)
{
color |= 0xFF000000;
}
if (alpha_read_mode.ReadMode == 2)
{
return color; // GX_READ_NONE
}
else if (alpha_read_mode.ReadMode == 1)
{
return color | 0xFF000000; // GX_READ_FF
}
else /*if(alpha_read_mode.ReadMode == 0)*/
{
return color & 0x00FFFFFF; // GX_READ_00
}
}
else // if (type == EFBAccessType::PeekZ)
{
// Depth buffer is inverted for improved precision near far plane
float depth = g_framebuffer_manager->PeekEFBDepth(x, y);
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
depth = 1.0f - depth;
u32 ret = 0;
if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
{
// if Z is in 16 bit format you must return a 16 bit integer
ret = MathUtil::Clamp<u32>(static_cast<u32>(depth * 65536.0f), 0, 0xFFFF);
}
else
{
ret = MathUtil::Clamp<u32>(static_cast<u32>(depth * 16777216.0f), 0, 0xFFFFFF);
}
return ret;
}
}
void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
{
if (type == EFBAccessType::PokeColor)
{
for (size_t i = 0; i < num_points; i++)
{
// Convert to expected format (BGRA->RGBA)
// TODO: Check alpha, depending on mode?
const EfbPokeData& point = points[i];
u32 color = ((point.data & 0xFF00FF00) | ((point.data >> 16) & 0xFF) |
((point.data << 16) & 0xFF0000));
g_framebuffer_manager->PokeEFBColor(point.x, point.y, color);
}
}
else // if (type == EFBAccessType::PokeZ)
{
for (size_t i = 0; i < num_points; i++)
{
// Convert to floating-point depth.
const EfbPokeData& point = points[i];
float depth = float(point.data & 0xFFFFFF) / 16777216.0f;
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
depth = 1.0f - depth;
g_framebuffer_manager->PokeEFBDepth(point.x, point.y, depth);
}
}
}
void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight,
@ -169,6 +311,8 @@ bool Renderer::CalculateTargetSize()
int new_efb_width = 0;
int new_efb_height = 0;
std::tie(new_efb_width, new_efb_height) = CalculateTargetScale(EFB_WIDTH, EFB_HEIGHT);
new_efb_width = std::max(new_efb_width, 1);
new_efb_height = std::max(new_efb_height, 1);
if (new_efb_width != m_target_width || new_efb_height != m_target_height)
{
@ -251,6 +395,11 @@ void Renderer::CheckForConfigChanges()
// Update texture cache settings with any changed options.
g_texture_cache->OnConfigChanged(g_ActiveConfig);
// Check for post-processing shader changes. Done up here as it doesn't affect anything outside
// the post-processor. Note that options are applied every frame, so no need to check those.
if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader)
m_post_processor->RecompileShader();
// Determine which (if any) settings have changed.
ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent();
u32 changed_bits = 0;
@ -278,13 +427,30 @@ void Renderer::CheckForConfigChanges()
// Notify the backend of the changes, if any.
OnConfigChanged(changed_bits);
// Framebuffer changed?
if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE |
CONFIG_CHANGE_BIT_TARGET_SIZE))
{
g_framebuffer_manager->RecreateEFBFramebuffer();
}
// Reload shaders if host config has changed.
if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES))
{
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
WaitForGPUIdle();
SetPipeline(nullptr);
g_vertex_manager->InvalidatePipelineObject();
g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples);
g_shader_cache->SetHostConfig(new_host_config);
g_shader_cache->Reload();
g_framebuffer_manager->RecompileShaders();
}
// Viewport and scissor rect have to be reset since they will be scaled differently.
if (changed_bits & CONFIG_CHANGE_BIT_TARGET_SIZE)
{
BPFunctions::SetViewport();
BPFunctions::SetScissor();
}
}
@ -389,6 +555,86 @@ void Renderer::ResizeSurface()
m_surface_resized.Set();
}
void Renderer::SetViewportAndScissor(const MathUtil::Rectangle<int>& rect, float min_depth,
float max_depth)
{
SetViewport(static_cast<float>(rect.left), static_cast<float>(rect.top),
static_cast<float>(rect.GetWidth()), static_cast<float>(rect.GetHeight()), min_depth,
max_depth);
SetScissorRect(rect);
}
void Renderer::ScaleTexture(AbstractFramebuffer* dst_framebuffer,
const MathUtil::Rectangle<int>& dst_rect,
const AbstractTexture* src_texture,
const MathUtil::Rectangle<int>& src_rect)
{
ASSERT(dst_framebuffer->GetColorFormat() == AbstractTextureFormat::RGBA8);
BeginUtilityDrawing();
// The shader needs to know the source rectangle.
const auto converted_src_rect = g_renderer->ConvertFramebufferRectangle(
src_rect, src_texture->GetWidth(), src_texture->GetHeight());
const float rcp_src_width = 1.0f / src_texture->GetWidth();
const float rcp_src_height = 1.0f / src_texture->GetHeight();
const std::array<float, 4> uniforms = {{converted_src_rect.left * rcp_src_width,
converted_src_rect.top * rcp_src_height,
converted_src_rect.GetWidth() * rcp_src_width,
converted_src_rect.GetHeight() * rcp_src_height}};
g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms));
// Discard if we're overwriting the whole thing.
if (static_cast<u32>(dst_rect.GetWidth()) == dst_framebuffer->GetWidth() &&
static_cast<u32>(dst_rect.GetHeight()) == dst_framebuffer->GetHeight())
{
SetAndDiscardFramebuffer(dst_framebuffer);
}
else
{
SetFramebuffer(dst_framebuffer);
}
SetViewportAndScissor(ConvertFramebufferRectangle(dst_rect, dst_framebuffer));
SetPipeline(dst_framebuffer->GetLayers() > 1 ? g_shader_cache->GetRGBA8StereoCopyPipeline() :
g_shader_cache->GetRGBA8CopyPipeline());
SetTexture(0, src_texture);
SetSamplerState(0, RenderState::GetLinearSamplerState());
Draw(0, 3);
EndUtilityDrawing();
if (dst_framebuffer->GetColorAttachment())
dst_framebuffer->GetColorAttachment()->FinishedRendering();
}
MathUtil::Rectangle<int>
Renderer::ConvertFramebufferRectangle(const MathUtil::Rectangle<int>& rect,
const AbstractFramebuffer* framebuffer)
{
return ConvertFramebufferRectangle(rect, framebuffer->GetWidth(), framebuffer->GetHeight());
}
MathUtil::Rectangle<int> Renderer::ConvertFramebufferRectangle(const MathUtil::Rectangle<int>& rect,
u32 fb_width, u32 fb_height)
{
MathUtil::Rectangle<int> ret = rect;
if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin)
{
ret.top = fb_height - rect.bottom;
ret.bottom = fb_height - rect.top;
}
return ret;
}
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
{
TargetRectangle result;
result.left = EFBToScaledX(rc.left);
result.top = EFBToScaledY(rc.top);
result.right = EFBToScaledX(rc.right);
result.bottom = EFBToScaledY(rc.bottom);
return result;
}
std::tuple<float, float> Renderer::ScaleToDisplayAspectRatio(const int width,
const int height) const
{
@ -700,7 +946,7 @@ bool Renderer::InitializeImGui()
vdecl.texcoords[0] = {VAR_FLOAT, 2, offsetof(ImDrawVert, uv), true, false};
vdecl.colors[0] = {VAR_UNSIGNED_BYTE, 4, offsetof(ImDrawVert, col), true, false};
vdecl.stride = sizeof(ImDrawVert);
m_imgui_vertex_format = g_vertex_manager->CreateNativeVertexFormat(vdecl);
m_imgui_vertex_format = CreateNativeVertexFormat(vdecl);
if (!m_imgui_vertex_format)
{
PanicAlert("Failed to create imgui vertex format");
@ -723,10 +969,10 @@ bool Renderer::InitializeImGui()
pconfig.vertex_format = m_imgui_vertex_format.get();
pconfig.vertex_shader = vertex_shader.get();
pconfig.pixel_shader = pixel_shader.get();
pconfig.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex;
pconfig.rasterization_state.primitive = PrimitiveType::Triangles;
pconfig.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex;
pconfig.blending_state.hex = RenderState::GetNoBlendingBlendState().hex;
pconfig.rasterization_state =
RenderState::GetCullBackFaceRasterizationState(PrimitiveType::Triangles);
pconfig.depth_state = RenderState::GetNoDepthTestingDepthState();
pconfig.blending_state = RenderState::GetNoBlendingBlendState();
pconfig.blending_state.blendenable = true;
pconfig.blending_state.srcfactor = BlendMode::SRCALPHA;
pconfig.blending_state.dstfactor = BlendMode::INVSRCALPHA;
@ -752,7 +998,7 @@ bool Renderer::InitializeImGui()
io.Fonts->GetTexDataAsRGBA32(&font_tex_pixels, &font_tex_width, &font_tex_height);
TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1,
AbstractTextureFormat::RGBA8, false);
AbstractTextureFormat::RGBA8, 0);
std::unique_ptr<AbstractTexture> font_tex = CreateTexture(font_tex_config);
if (!font_tex)
{
@ -799,10 +1045,8 @@ void Renderer::BeginImGuiFrame()
ImGui::NewFrame();
}
void Renderer::RenderImGui()
void Renderer::DrawImGui()
{
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
if (!draw_data)
return;
@ -842,9 +1086,11 @@ void Renderer::RenderImGui()
continue;
}
SetScissorRect(MathUtil::Rectangle<int>(
static_cast<int>(cmd.ClipRect.x), static_cast<int>(cmd.ClipRect.y),
static_cast<int>(cmd.ClipRect.z), static_cast<int>(cmd.ClipRect.w)));
SetScissorRect(ConvertFramebufferRectangle(
MathUtil::Rectangle<int>(
static_cast<int>(cmd.ClipRect.x), static_cast<int>(cmd.ClipRect.y),
static_cast<int>(cmd.ClipRect.z), static_cast<int>(cmd.ClipRect.w)),
m_current_framebuffer));
SetTexture(0, reinterpret_cast<const AbstractTexture*>(cmd.TextureId));
DrawIndexed(base_index, cmd.ElemCount, base_vertex);
base_index += cmd.ElemCount;
@ -859,7 +1105,10 @@ std::unique_lock<std::mutex> Renderer::GetImGuiLock()
void Renderer::BeginUIFrame()
{
ResetAPIState();
if (IsHeadless())
return;
BeginUtilityDrawing();
BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f});
}
@ -867,16 +1116,19 @@ void Renderer::EndUIFrame()
{
{
auto lock = GetImGuiLock();
RenderImGui();
ImGui::Render();
}
if (!IsHeadless())
{
DrawImGui();
std::lock_guard<std::mutex> guard(m_swap_mutex);
PresentBackbuffer();
EndUtilityDrawing();
}
BeginImGuiFrame();
RestoreAPIState();
}
void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
@ -946,32 +1198,34 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
// with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this.
g_vertex_manager->Flush();
// Render the XFB to the screen.
ResetAPIState();
BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f});
UpdateDrawRectangle();
RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect);
// Hold the imgui lock while we're presenting.
// It's only to prevent races on inputs anyway, at this point.
// Render any UI elements to the draw list.
{
auto lock = GetImGuiLock();
DrawDebugText();
OSD::DrawMessages();
RenderImGui();
ImGui::Render();
}
// Present to the window system.
// Render the XFB to the screen.
BeginUtilityDrawing();
if (!IsHeadless())
{
std::lock_guard<std::mutex> guard(m_swap_mutex);
PresentBackbuffer();
}
BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}});
UpdateDrawRectangle();
RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect);
DrawImGui();
// Update the window size based on the frame that was just rendered.
// Due to depending on guest state, we need to call this every frame.
SetWindowSize(texture_config.width, texture_config.height);
// Present to the window system.
{
std::lock_guard<std::mutex> guard(m_swap_mutex);
PresentBackbuffer();
}
// Update the window size based on the frame that was just rendered.
// Due to depending on guest state, we need to call this every frame.
SetWindowSize(texture_config.width, texture_config.height);
}
m_fps_counter.Update();
@ -984,12 +1238,11 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
if (IsFrameDumping())
DumpCurrentFrame();
frameCount++;
GFX_DEBUGGER_PAUSE_AT(NEXT_FRAME, true);
// Begin new frame
m_frame_count++;
stats.ResetFrame();
g_shader_cache->RetrieveAsyncShaders();
g_vertex_manager->OnEndFrame();
BeginImGuiFrame();
// We invalidate the pipeline object at the start of the frame.
@ -1003,13 +1256,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
g_texture_cache->FlushEFBCopies();
// Remove stale EFB/XFB copies.
g_texture_cache->Cleanup(frameCount);
g_texture_cache->Cleanup(m_frame_count);
// Handle any config changes, this gets propogated to the backend.
CheckForConfigChanges();
g_Config.iSaveTargetId = 0;
RestoreAPIState();
EndUtilityDrawing();
Core::Callback_VideoCopiedToXFB(true);
}
@ -1028,6 +1281,24 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
}
}
void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc)
{
const auto target_rc = GetTargetRectangle();
if (g_ActiveConfig.stereo_mode == StereoMode::SBS ||
g_ActiveConfig.stereo_mode == StereoMode::TAB)
{
TargetRectangle left_rc, right_rc;
std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc);
m_post_processor->BlitFromTexture(left_rc, rc, texture, 0);
m_post_processor->BlitFromTexture(right_rc, rc, texture, 1);
}
else
{
m_post_processor->BlitFromTexture(target_rc, rc, texture, 0);
}
}
bool Renderer::IsFrameDumping()
{
if (m_screenshot_request.IsSet())
@ -1040,15 +1311,6 @@ bool Renderer::IsFrameDumping()
}
void Renderer::DumpCurrentFrame()
{
// Scale/render to frame dump texture.
RenderFrameDump();
// Queue a readback for the next frame.
QueueFrameDumpReadback();
}
void Renderer::RenderFrameDump()
{
int target_width, target_height;
if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless())
@ -1063,50 +1325,76 @@ void Renderer::RenderFrameDump()
m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().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 ||
m_frame_dump_render_texture->GetConfig().width != static_cast<u32>(target_width) ||
m_frame_dump_render_texture->GetConfig().height != static_cast<u32>(target_height))
// We only need to render a copy if we need to stretch/scale the XFB copy.
const AbstractTexture* source_tex = m_last_xfb_texture;
MathUtil::Rectangle<int> source_rect = m_last_xfb_region;
if (source_rect.GetWidth() != target_width || source_rect.GetHeight() != target_height)
{
// Recreate texture objects. Release before creating so we don't temporarily use twice the RAM.
TextureConfig config(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, true);
m_frame_dump_render_texture.reset();
m_frame_dump_render_texture = CreateTexture(config);
ASSERT(m_frame_dump_render_texture);
if (!CheckFrameDumpRenderTexture(target_width, target_height))
return;
source_tex = m_frame_dump_render_texture.get();
source_rect = MathUtil::Rectangle<int>(0, 0, target_width, target_height);
ScaleTexture(m_frame_dump_render_framebuffer.get(), source_rect, m_last_xfb_texture,
m_last_xfb_region);
}
// Scaling is likely to occur here, but if possible, do a bit-for-bit copy.
if (m_last_xfb_region.GetWidth() != target_width ||
m_last_xfb_region.GetHeight() != target_height)
{
m_frame_dump_render_texture->ScaleRectangleFromTexture(
m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height});
}
else
{
m_frame_dump_render_texture->CopyRectangleFromTexture(
m_last_xfb_texture, m_last_xfb_region, 0, 0,
EFBRectangle{0, 0, target_width, target_height}, 0, 0);
}
}
void Renderer::QueueFrameDumpReadback()
{
// Index 0 was just sent to AVI dump. Swap with the second texture.
if (m_frame_dump_readback_textures[0])
std::swap(m_frame_dump_readback_textures[0], m_frame_dump_readback_textures[1]);
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
if (!rbtex || rbtex->GetConfig() != m_frame_dump_render_texture->GetConfig())
{
rbtex = CreateStagingTexture(StagingTextureType::Readback,
m_frame_dump_render_texture->GetConfig());
}
if (!CheckFrameDumpReadbackTexture(target_width, target_height))
return;
const auto converted_region =
ConvertFramebufferRectangle(source_rect, source_tex->GetWidth(), source_tex->GetHeight());
m_frame_dump_readback_textures[0]->CopyFromTexture(
source_tex, converted_region, 0, 0,
MathUtil::Rectangle<int>(0, 0, target_width, target_height));
m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks);
m_last_frame_exported = true;
rbtex->CopyFromTexture(m_frame_dump_render_texture.get(), 0, 0);
}
bool Renderer::CheckFrameDumpRenderTexture(u32 target_width, u32 target_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 && m_frame_dump_render_texture->GetWidth() == target_width &&
m_frame_dump_render_texture->GetHeight() == target_height)
{
return true;
}
// Recreate texture, but release before creating so we don't temporarily use twice the RAM.
m_frame_dump_render_framebuffer.reset();
m_frame_dump_render_texture.reset();
m_frame_dump_render_texture =
CreateTexture(TextureConfig(target_width, target_height, 1, 1, 1,
AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget));
if (!m_frame_dump_render_texture)
{
PanicAlert("Failed to allocate frame dump render texture");
return false;
}
m_frame_dump_render_framebuffer = CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr);
ASSERT(m_frame_dump_render_framebuffer);
return true;
}
bool Renderer::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height)
{
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
if (rbtex && rbtex->GetWidth() == target_width && rbtex->GetHeight() == target_height)
return true;
rbtex.reset();
rbtex = CreateStagingTexture(
StagingTextureType::Readback,
TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0));
if (!rbtex)
return false;
return true;
}
void Renderer::FlushFrameDump()
@ -1151,6 +1439,7 @@ void Renderer::ShutdownFrameDumping()
m_frame_dump_start.Set();
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
m_frame_dump_render_framebuffer.reset();
m_frame_dump_render_texture.reset();
for (auto& tex : m_frame_dump_readback_textures)
tex.reset();