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:
parent
725dcea05a
commit
e1cbb50c64
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue