GPU: Fix various capture/screenshot issues

- Simplify functions for rendering display.

- Post-processing should only when the target size matches the window.
  Otherwise the shaders are constantly recompiled.

- Include border overlay in capture/screenshots when above condition
  is satisfied.

- Relax video alignment size when using screen resolution + auto.

- Fix "Internal Resolution (Uncorrected)" capture mode.
This commit is contained in:
Stenzek 2025-01-19 13:41:15 +10:00
parent 725dcea05a
commit e1cbb50c64
No known key found for this signature in database
7 changed files with 147 additions and 138 deletions

View File

@ -653,25 +653,39 @@ void GPUBackend::UpdateStatistics(u32 frame_count)
ResetStatistics();
}
bool GPUBackend::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, Image* out_image)
bool GPUBackend::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio, Image* out_image,
Error* error)
{
bool result;
GPUThread::RunOnBackend(
[width, height, postfx, out_image, &result](GPUBackend* backend) {
[width, height, postfx, apply_aspect_ratio, out_image, error, &result](GPUBackend* backend) {
if (!backend)
{
Error::SetStringView(error, "No GPU backend.");
result = false;
return;
}
GSVector4i draw_rect, display_rect;
backend->m_presenter.CalculateDrawRect(static_cast<s32>(width), static_cast<s32>(height), true, true,
&display_rect, &draw_rect);
// Post-processing requires that the size match the window.
const bool really_postfx = postfx && g_gpu_device->HasMainSwapChain();
u32 image_width, image_height;
if (really_postfx)
{
image_width = g_gpu_device->GetMainSwapChain()->GetWidth();
image_height = g_gpu_device->GetMainSwapChain()->GetHeight();
}
else
{
// Crop it if border overlay isn't enabled.
GSVector4i draw_rect, display_rect;
backend->GetPresenter().CalculateDrawRect(static_cast<s32>(width), static_cast<s32>(height), apply_aspect_ratio,
&display_rect, &draw_rect);
image_width = static_cast<u32>(display_rect.width());
image_height = static_cast<u32>(display_rect.height());
}
// Crop it.
const u32 cropped_width = static_cast<u32>(display_rect.width());
const u32 cropped_height = static_cast<u32>(display_rect.height());
draw_rect = draw_rect.sub32(display_rect.xyxy());
display_rect = display_rect.sub32(display_rect.xyxy());
result = backend->m_presenter.RenderScreenshotToBuffer(cropped_width, cropped_height, display_rect, draw_rect,
postfx, out_image);
result = backend->GetPresenter().RenderScreenshotToBuffer(image_width, image_height, really_postfx,
apply_aspect_ratio, out_image, error);
backend->RestoreDeviceContext();
},
true, false);
@ -687,19 +701,29 @@ void GPUBackend::RenderScreenshotToFile(const std::string_view path, DisplayScre
if (!backend)
return;
u32 width, height;
GSVector4i display_rect, draw_rect;
backend->m_presenter.CalculateScreenshotSize(mode, &width, &height, &display_rect, &draw_rect);
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution);
if (width == 0 || height == 0)
const GSVector2i size = backend->GetPresenter().CalculateScreenshotSize(mode);
if (size.x == 0 || size.y == 0)
return;
std::string osd_key;
if (show_osd_message)
osd_key = fmt::format("ScreenshotSaver_{}", path);
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution);
const bool apply_aspect_ratio = (mode != DisplayScreenshotMode::UncorrectedInternalResolution);
Error error;
Image image;
if (!backend->m_presenter.RenderScreenshotToBuffer(width, height, display_rect, draw_rect, !internal_resolution,
&image))
if (!backend->m_presenter.RenderScreenshotToBuffer(size.x, size.y, !internal_resolution, apply_aspect_ratio,
&image, &error))
{
ERROR_LOG("Failed to render {}x{} screenshot", width, height);
ERROR_LOG("Failed to render {}x{} screenshot: {}", size.x, size.y, error.GetDescription());
if (show_osd_message)
{
Host::AddIconOSDWarning(
std::move(osd_key), ICON_EMOJI_WARNING,
fmt::format(TRANSLATE_FS("GPU", "Failed to save screenshot:\n{}"), error.GetDescription()));
}
backend->RestoreDeviceContext();
return;
}
@ -707,19 +731,23 @@ void GPUBackend::RenderScreenshotToFile(const std::string_view path, DisplayScre
// no more GPU calls
backend->RestoreDeviceContext();
Error error;
auto fp = FileSystem::OpenManagedCFile(path.c_str(), "wb", &error);
if (!fp)
{
ERROR_LOG("Can't open file '{}': {}", Path::GetFileName(path), error.GetDescription());
if (show_osd_message)
{
Host::AddIconOSDWarning(
std::move(osd_key), ICON_EMOJI_WARNING,
fmt::format(TRANSLATE_FS("GPU", "Failed to save screenshot:\n{}"), error.GetDescription()));
}
return;
}
std::string osd_key;
if (show_osd_message)
{
// Use a 60 second timeout to give it plenty of time to actually save.
osd_key = fmt::format("ScreenshotSaver_{}", path);
Host::AddIconOSDMessage(osd_key, ICON_EMOJI_CAMERA_WITH_FLASH,
fmt::format(TRANSLATE_FS("GPU", "Saving screenshot to '{}'."), Path::GetFileName(path)),
60.0f);

View File

@ -58,7 +58,8 @@ public:
static std::unique_ptr<GPUBackend> CreateSoftwareBackend(GPUPresenter& presenter);
static std::unique_ptr<GPUBackend> CreateNullBackend(GPUPresenter& presenter);
static bool RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, Image* out_image);
static bool RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio, Image* out_image,
Error* error);
static void RenderScreenshotToFile(const std::string_view path, DisplayScreenshotMode mode, u8 quality,
bool show_osd_message);

View File

@ -746,8 +746,7 @@ u32 GPU_HW::CalculateResolutionScale() const
{
GSVector4i display_rect, draw_rect;
m_presenter.CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(),
g_gpu_device->GetMainSwapChain()->GetHeight(), true, true, &display_rect,
&draw_rect);
g_gpu_device->GetMainSwapChain()->GetHeight(), true, &display_rect, &draw_rect);
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
// anamorphic aspect ratio.

View File

@ -377,65 +377,60 @@ void GPUPresenter::SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y
m_display_texture_view_height = view_height;
}
GPUDevice::PresentResult GPUPresenter::PresentDisplay()
GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const GSVector2i target_size, bool postfx,
bool apply_aspect_ratio)
{
DebugAssert(g_gpu_device->HasMainSwapChain());
GL_SCOPE_FMT("RenderDisplay: {}x{}", target_size.x, target_size.y);
u32 display_area_width = g_gpu_device->GetMainSwapChain()->GetWidth();
u32 display_area_height = g_gpu_device->GetMainSwapChain()->GetHeight();
if (m_display_texture)
m_display_texture->MakeReadyForSampling();
DebugAssert(target || g_gpu_device->HasMainSwapChain());
DebugAssert(!postfx || target_size.eq(g_gpu_device->GetMainSwapChain()->GetSizeVec()));
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
const WindowInfo::PreRotation prerotation = target ? WindowInfo::PreRotation::Identity : swap_chain->GetPreRotation();
const bool is_vram_view = g_gpu_settings.gpu_show_vram;
const bool have_overlay = (postfx && !is_vram_view && HasBorderOverlay());
const bool have_prerotation = (prerotation != WindowInfo::PreRotation::Identity);
GL_INS(have_overlay ? "Overlay is ENABLED" : "Overlay is disabled");
GL_INS_FMT("Prerotation: {}", static_cast<u32>(prerotation));
GL_INS_FMT("Final target size: {}x{}", target_size.x, target_size.y);
// Compute draw area.
GSVector4i display_rect;
GSVector4i draw_rect;
GSVector4i overlay_display_rect = GSVector4i::zero();
GSVector4i overlay_rect = GSVector4i::zero();
if (m_border_overlay_texture)
if (have_overlay)
{
overlay_rect = GSVector4i::rfit(GSVector4i(0, 0, display_area_width, display_area_height),
m_border_overlay_texture->GetSizeVec());
overlay_rect = GSVector4i::rfit(GSVector4i::loadh(target_size), m_border_overlay_texture->GetSizeVec());
const GSVector2 scale = GSVector2(overlay_rect.rsize()) / GSVector2(m_border_overlay_texture->GetSizeVec());
overlay_display_rect =
GSVector4i(GSVector4(m_border_overlay_display_rect) * GSVector4::xyxy(scale)).add32(overlay_rect.xyxy());
display_area_width = overlay_display_rect.width();
display_area_height = overlay_display_rect.height();
// Draw to the overlay area instead of the whole screen.
CalculateDrawRect(overlay_display_rect.width(), overlay_display_rect.height(), apply_aspect_ratio, &display_rect,
&draw_rect);
// Apply overlay area offset.
display_rect = display_rect.add32(overlay_display_rect.xyxy());
draw_rect = draw_rect.add32(overlay_display_rect.xyxy());
}
else
{
CalculateDrawRect(target_size.x, target_size.y, apply_aspect_ratio, &display_rect, &draw_rect);
}
GSVector4i display_rect;
GSVector4i draw_rect;
CalculateDrawRect(display_area_width, display_area_height, !g_gpu_settings.gpu_show_vram, true, &display_rect,
&draw_rect);
display_rect = display_rect.add32(overlay_display_rect.xyxy());
draw_rect = draw_rect.add32(overlay_display_rect.xyxy());
return RenderDisplay(nullptr, overlay_rect, overlay_display_rect, display_rect, draw_rect,
!g_gpu_settings.gpu_show_vram);
}
GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect,
const GSVector4i overlay_display_rect,
const GSVector4i display_rect, const GSVector4i draw_rect,
bool postfx)
{
GL_SCOPE_FMT("RenderDisplay: {}", draw_rect);
if (m_display_texture)
m_display_texture->MakeReadyForSampling();
// There's a bunch of scenarios where we need to use intermediate buffers.
// If we have post-processing and overlays enabled, postfx needs to happen on an intermediate buffer first.
// If pre-rotation is enabled with post-processing, we need to draw to an intermediate buffer, and apply the
// rotation at the end.
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
const WindowInfo::PreRotation prerotation = target ? WindowInfo::PreRotation::Identity : swap_chain->GetPreRotation();
const bool have_overlay = static_cast<bool>(m_border_overlay_texture);
const bool have_prerotation = (prerotation != WindowInfo::PreRotation::Identity);
const GSVector2i target_size = target ? target->GetSizeVec() : swap_chain->GetSizeVec();
GL_INS(have_overlay ? "Overlay is ENABLED" : "Overlay is disabled");
GL_INS_FMT("Prerotation: {}", static_cast<u32>(prerotation));
GL_INS_FMT("Final target size: {}x{}", target_size.x, target_size.y);
// Postfx active?
const GSVector2i postfx_size = have_overlay ? overlay_display_rect.rsize() : target_size;
const bool really_postfx = (postfx && m_display_postfx && m_display_postfx->IsActive() && m_display_postfx &&
m_display_postfx->CheckTargets(m_present_format, postfx_size.x, postfx_size.y));
const bool really_postfx =
(postfx && !is_vram_view && m_display_postfx && m_display_postfx->IsActive() && m_display_postfx &&
m_display_postfx->CheckTargets(m_present_format, postfx_size.x, postfx_size.y));
GL_INS(really_postfx ? "Post-processing is ENABLED" : "Post-processing is disabled");
GL_INS_FMT("Post-processing render target size: {}x{}", postfx_size.x, postfx_size.y);
@ -708,16 +703,11 @@ void GPUPresenter::SendDisplayToMediaCapture(MediaCapture* cap)
const bool apply_aspect_ratio =
(g_gpu_settings.display_screenshot_mode != DisplayScreenshotMode::UncorrectedInternalResolution);
const bool postfx = (g_gpu_settings.display_screenshot_mode != DisplayScreenshotMode::InternalResolution);
GSVector4i display_rect, draw_rect;
CalculateDrawRect(target->GetWidth(), target->GetHeight(), !g_gpu_settings.gpu_show_vram, apply_aspect_ratio,
&display_rect, &draw_rect);
const bool postfx =
(g_gpu_settings.display_screenshot_mode == DisplayScreenshotMode::ScreenResolution &&
g_gpu_device->HasMainSwapChain() && target->GetSizeVec().eq(g_gpu_device->GetMainSwapChain()->GetSizeVec()));
// Not cleared by RenderDisplay().
g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR);
if (RenderDisplay(target, GSVector4i::zero(), GSVector4i::zero(), display_rect, draw_rect, postfx) !=
GPUDevice::PresentResult::OK ||
if (RenderDisplay(target, target->GetSizeVec(), postfx, apply_aspect_ratio) != GPUDevice::PresentResult::OK ||
!cap->DeliverVideoFrame(target)) [[unlikely]]
{
WARNING_LOG("Failed to render/deliver video capture frame.");
@ -907,7 +897,7 @@ bool GPUPresenter::ApplyChromaSmoothing()
return true;
}
void GPUPresenter::CalculateDrawRect(s32 window_width, s32 window_height, bool apply_rotation, bool apply_aspect_ratio,
void GPUPresenter::CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio,
GSVector4i* display_rect, GSVector4i* draw_rect) const
{
const bool integer_scale = (g_gpu_settings.display_scaling == DisplayScalingMode::NearestInteger ||
@ -919,9 +909,10 @@ void GPUPresenter::CalculateDrawRect(s32 window_width, s32 window_height, bool a
const s32 display_origin_top = show_vram ? 0 : m_display_origin_top;
const u32 display_vram_width = show_vram ? VRAM_WIDTH : m_display_vram_width;
const u32 display_vram_height = show_vram ? VRAM_HEIGHT : m_display_vram_height;
const float display_pixel_aspect_ratio = show_vram ? 1.0f : m_display_pixel_aspect_ratio;
const float display_pixel_aspect_ratio = (show_vram || !apply_aspect_ratio) ? 1.0f : m_display_pixel_aspect_ratio;
const DisplayRotation display_rotation = show_vram ? DisplayRotation::Normal : g_gpu_settings.display_rotation;
GPU::CalculateDrawRect(window_width, window_height, display_width, display_height, display_origin_left,
display_origin_top, display_vram_width, display_vram_height, g_gpu_settings.display_rotation,
display_origin_top, display_vram_width, display_vram_height, display_rotation,
g_gpu_settings.display_alignment, display_pixel_aspect_ratio,
g_gpu_settings.display_stretch_vertically, integer_scale, display_rect, draw_rect);
}
@ -954,16 +945,17 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
ImGuiManager::RenderDebugWindows();
}
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
const GPUDevice::PresentResult pres =
skip_present ?
GPUDevice::PresentResult::SkipPresent :
(presenter ? presenter->PresentDisplay() : g_gpu_device->BeginPresent(g_gpu_device->GetMainSwapChain()));
skip_present ? GPUDevice::PresentResult::SkipPresent :
(presenter ? presenter->RenderDisplay(nullptr, swap_chain->GetSizeVec(), true, true) :
g_gpu_device->BeginPresent(swap_chain));
if (pres == GPUDevice::PresentResult::OK)
{
if (presenter)
presenter->m_skipped_present_count = 0;
g_gpu_device->RenderImGui(g_gpu_device->GetMainSwapChain());
g_gpu_device->RenderImGui(swap_chain);
const GPUDevice::Features features = g_gpu_device->GetFeatures();
const bool scheduled_present = (present_time != 0);
@ -977,7 +969,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
SleepUntilPresentTime(present_time);
}
g_gpu_device->EndPresent(g_gpu_device->GetMainSwapChain(), explicit_present, timed_present ? present_time : 0);
g_gpu_device->EndPresent(swap_chain, explicit_present, timed_present ? present_time : 0);
if (g_gpu_device->IsGPUTimingEnabled())
PerformanceCounters::AccumulateGPUTime();
@ -985,7 +977,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
if (explicit_present)
{
SleepUntilPresentTime(present_time);
g_gpu_device->SubmitPresent(g_gpu_device->GetMainSwapChain());
g_gpu_device->SubmitPresent(swap_chain);
}
}
else
@ -1029,8 +1021,8 @@ void GPUPresenter::SleepUntilPresentTime(u64 present_time)
#endif
}
bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect,
const GSVector4i draw_rect, bool postfx, Image* out_image)
bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio,
Image* out_image, Error* error)
{
const ImageFormat image_format = GPUTexture::GetImageFormatForTextureFormat(m_present_format);
if (image_format == ImageFormat::None)
@ -1042,24 +1034,26 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVecto
return false;
g_gpu_device->ClearRenderTarget(render_texture.get(), GPUDevice::DEFAULT_CLEAR_COLOR);
// TODO: this should use copy shader instead.
RenderDisplay(render_texture.get(), GSVector4i::zero(), GSVector4i::zero(), display_rect, draw_rect, postfx);
if (RenderDisplay(render_texture.get(), render_texture->GetSizeVec(), postfx, apply_aspect_ratio) !=
GPUDevice::PresentResult::OK)
{
Error::SetStringView(error, "RenderDisplay() failed");
return false;
}
Image image(width, height, image_format);
Error error;
std::unique_ptr<GPUDownloadTexture> dltex;
if (g_gpu_device->GetFeatures().memory_import)
{
dltex = g_gpu_device->CreateDownloadTexture(width, height, m_present_format, image.GetPixels(),
image.GetStorageSize(), image.GetPitch(), &error);
image.GetStorageSize(), image.GetPitch(), error);
}
if (!dltex)
{
if (!(dltex = g_gpu_device->CreateDownloadTexture(width, height, m_present_format, &error)))
if (!(dltex = g_gpu_device->CreateDownloadTexture(width, height, m_present_format, error)))
{
ERROR_LOG("Failed to create {}x{} download texture: {}", width, height, error.GetDescription());
Error::AddPrefixFmt(error, "Failed to create {}x{} download texture: ", width, height);
return false;
}
}
@ -1072,8 +1066,7 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVecto
return true;
}
void GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height,
GSVector4i* display_rect, GSVector4i* draw_rect) const
GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) const
{
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution || g_gpu_settings.gpu_show_vram);
if (internal_resolution && m_display_texture_view_width != 0 && m_display_texture_view_height != 0)
@ -1098,24 +1091,16 @@ void GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* widt
f_width = f_width / (f_height / max_texture_size);
}
*width = static_cast<u32>(std::ceil(f_width));
*height = static_cast<u32>(std::ceil(f_height));
return GSVector2i(static_cast<s32>(std::ceil(f_width)), static_cast<s32>(std::ceil(f_height)));
}
else // if (mode == DisplayScreenshotMode::UncorrectedInternalResolution)
{
*width = m_display_texture_view_width;
*height = m_display_texture_view_height;
return GSVector2i(m_display_texture_view_width, m_display_texture_view_height);
}
// Remove padding, it's not part of the framebuffer.
*draw_rect = GSVector4i(0, 0, static_cast<s32>(*width), static_cast<s32>(*height));
*display_rect = *draw_rect;
}
else
{
*width = g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetWidth() : 1;
*height = g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetHeight() : 1;
CalculateDrawRect(*width, *height, true, !g_settings.gpu_show_vram, display_rect, draw_rect);
return g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetSizeVec() : GSVector2i(1, 1);
}
}

View File

@ -44,6 +44,7 @@ public:
ALWAYS_INLINE s32 GetDisplayTextureViewHeight() const { return m_display_texture_view_height; }
ALWAYS_INLINE GPUTexture* GetDisplayTexture() const { return m_display_texture; }
ALWAYS_INLINE bool HasDisplayTexture() const { return m_display_texture; }
ALWAYS_INLINE bool HasBorderOverlay() const { return static_cast<bool>(m_border_overlay_texture); }
bool Initialize(Error* error);
@ -59,16 +60,15 @@ public:
bool ApplyChromaSmoothing();
/// Helper function for computing the draw rectangle in a larger window.
void CalculateDrawRect(s32 window_width, s32 window_height, bool apply_rotation, bool apply_aspect_ratio,
GSVector4i* display_rect, GSVector4i* draw_rect) const;
void CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio, GSVector4i* display_rect,
GSVector4i* draw_rect) const;
/// Helper function for computing screenshot bounds.
void CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height, GSVector4i* display_rect,
GSVector4i* draw_rect) const;
GSVector2i CalculateScreenshotSize(DisplayScreenshotMode mode) const;
/// Renders the display, optionally with postprocessing to the specified image.
bool RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect,
bool postfx, Image* out_image);
bool RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio, Image* out_image,
Error* error);
/// Sends the current frame to media capture.
void SendDisplayToMediaCapture(MediaCapture* cap);
@ -98,14 +98,10 @@ private:
static void SleepUntilPresentTime(u64 present_time);
/// Draws the current display texture, with any post-processing.
GPUDevice::PresentResult PresentDisplay();
bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error);
GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect,
const GSVector4i overlay_display_rect, const GSVector4i display_rect,
const GSVector4i draw_rect, bool postfx);
GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector2i target_size, bool postfx,
bool apply_aspect_ratio);
void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend,
DisplayRotation rotation, WindowInfo::PreRotation prerotation);

View File

@ -3257,7 +3257,9 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
// save screenshot
if (screenshot_size > 0)
{
if (GPUBackend::RenderScreenshotToBuffer(screenshot_size, screenshot_size, false, &buffer->screenshot))
Error screenshot_error;
if (GPUBackend::RenderScreenshotToBuffer(screenshot_size, screenshot_size, false, true, &buffer->screenshot,
&screenshot_error))
{
if (g_gpu_device->UsesLowerLeftOrigin())
buffer->screenshot.FlipY();
@ -3265,12 +3267,11 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
// Ensure it's RGBA8.
if (buffer->screenshot.GetFormat() != ImageFormat::RGBA8)
{
Error convert_error;
std::optional<Image> screenshot_rgba8 = buffer->screenshot.ConvertToRGBA8(&convert_error);
std::optional<Image> screenshot_rgba8 = buffer->screenshot.ConvertToRGBA8(&screenshot_error);
if (!screenshot_rgba8.has_value())
{
ERROR_LOG("Failed to convert {} screenshot to RGBA8: {}",
Image::GetFormatName(buffer->screenshot.GetFormat()), convert_error.GetDescription());
Image::GetFormatName(buffer->screenshot.GetFormat()), screenshot_error.GetDescription());
buffer->screenshot.Invalidate();
}
else
@ -3281,8 +3282,8 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
}
else
{
WARNING_LOG("Failed to save {}x{} screenshot for save state due to render/conversion failure", screenshot_size,
screenshot_size);
WARNING_LOG("Failed to save {}x{} screenshot for save state: {}", screenshot_size, screenshot_size,
screenshot_error.GetDescription());
}
}
@ -5347,14 +5348,16 @@ bool System::StartMediaCapture(std::string path)
{
// need to query this on the GPU thread
GPUThread::RunOnBackend(
[path = std::move(path), capture_audio](GPUBackend* backend) mutable {
[path = std::move(path), capture_audio, mode = g_settings.display_screenshot_mode](GPUBackend* backend) mutable {
if (!backend)
return;
GSVector4i unused_display_rect, unused_draw_rect;
u32 video_width, video_height;
backend->GetPresenter().CalculateScreenshotSize(DisplayScreenshotMode::InternalResolution, &video_width,
&video_height, &unused_display_rect, &unused_draw_rect);
// Prefer aligning for non-window size.
const GSVector2i video_size = backend->GetPresenter().CalculateScreenshotSize(mode);
u32 video_width = static_cast<u32>(video_size.x);
u32 video_height = static_cast<u32>(video_size.y);
if (mode != DisplayScreenshotMode::ScreenResolution)
MediaCapture::AdjustVideoSize(&video_width, &video_height);
// fire back to the CPU thread to actually start the capture
Host::RunOnCPUThread([path = std::move(path), capture_audio, video_width, video_height]() mutable {
@ -5369,6 +5372,7 @@ bool System::StartMediaCapture(std::string path)
Host::GetUIntSettingValue("MediaCapture", "VideoWidth", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH);
u32 video_height =
Host::GetUIntSettingValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
MediaCapture::AdjustVideoSize(&video_width, &video_height);
return StartMediaCapture(std::move(path), capture_video, capture_audio, video_width, video_height);
}
@ -5385,8 +5389,6 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur
const WindowInfo& main_window_info = GPUThread::GetRenderWindowInfo();
const GPUTexture::Format capture_format =
main_window_info.IsSurfaceless() ? GPUTexture::Format::RGBA8 : main_window_info.surface_format;
if (capture_video)
MediaCapture::AdjustVideoSize(&video_width, &video_height);
// TODO: Render anamorphic capture instead?
constexpr float aspect = 1.0f;

View File

@ -200,9 +200,7 @@ bool MediaCaptureBase::BeginCapture(float fps, float aspect, u32 width, u32 heig
Error::SetStringView(error, "No path specified.");
return false;
}
else if (capture_video &&
(fps == 0.0f || m_video_width == 0 || !Common::IsAlignedPow2(m_video_width, VIDEO_WIDTH_ALIGNMENT) ||
m_video_height == 0 || !Common::IsAlignedPow2(m_video_height, VIDEO_HEIGHT_ALIGNMENT)))
else if (capture_video && (fps == 0.0f || m_video_width == 0 || m_video_height == 0))
{
Error::SetStringView(error, "Invalid video dimensions/rate.");
return false;