mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-07 20:15:32 +00:00
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();
|
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;
|
bool result;
|
||||||
GPUThread::RunOnBackend(
|
GPUThread::RunOnBackend(
|
||||||
[width, height, postfx, out_image, &result](GPUBackend* backend) {
|
[width, height, postfx, apply_aspect_ratio, out_image, error, &result](GPUBackend* backend) {
|
||||||
if (!backend)
|
if (!backend)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "No GPU backend.");
|
||||||
|
result = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GSVector4i draw_rect, display_rect;
|
// Post-processing requires that the size match the window.
|
||||||
backend->m_presenter.CalculateDrawRect(static_cast<s32>(width), static_cast<s32>(height), true, true,
|
const bool really_postfx = postfx && g_gpu_device->HasMainSwapChain();
|
||||||
&display_rect, &draw_rect);
|
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.
|
result = backend->GetPresenter().RenderScreenshotToBuffer(image_width, image_height, really_postfx,
|
||||||
const u32 cropped_width = static_cast<u32>(display_rect.width());
|
apply_aspect_ratio, out_image, error);
|
||||||
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);
|
|
||||||
backend->RestoreDeviceContext();
|
backend->RestoreDeviceContext();
|
||||||
},
|
},
|
||||||
true, false);
|
true, false);
|
||||||
@ -687,19 +701,29 @@ void GPUBackend::RenderScreenshotToFile(const std::string_view path, DisplayScre
|
|||||||
if (!backend)
|
if (!backend)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
u32 width, height;
|
const GSVector2i size = backend->GetPresenter().CalculateScreenshotSize(mode);
|
||||||
GSVector4i display_rect, draw_rect;
|
if (size.x == 0 || size.y == 0)
|
||||||
backend->m_presenter.CalculateScreenshotSize(mode, &width, &height, &display_rect, &draw_rect);
|
|
||||||
|
|
||||||
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution);
|
|
||||||
if (width == 0 || height == 0)
|
|
||||||
return;
|
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;
|
Image image;
|
||||||
if (!backend->m_presenter.RenderScreenshotToBuffer(width, height, display_rect, draw_rect, !internal_resolution,
|
if (!backend->m_presenter.RenderScreenshotToBuffer(size.x, size.y, !internal_resolution, apply_aspect_ratio,
|
||||||
&image))
|
&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();
|
backend->RestoreDeviceContext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -707,19 +731,23 @@ void GPUBackend::RenderScreenshotToFile(const std::string_view path, DisplayScre
|
|||||||
// no more GPU calls
|
// no more GPU calls
|
||||||
backend->RestoreDeviceContext();
|
backend->RestoreDeviceContext();
|
||||||
|
|
||||||
Error error;
|
|
||||||
auto fp = FileSystem::OpenManagedCFile(path.c_str(), "wb", &error);
|
auto fp = FileSystem::OpenManagedCFile(path.c_str(), "wb", &error);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Can't open file '{}': {}", Path::GetFileName(path), error.GetDescription());
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string osd_key;
|
|
||||||
if (show_osd_message)
|
if (show_osd_message)
|
||||||
{
|
{
|
||||||
// Use a 60 second timeout to give it plenty of time to actually save.
|
// 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,
|
Host::AddIconOSDMessage(osd_key, ICON_EMOJI_CAMERA_WITH_FLASH,
|
||||||
fmt::format(TRANSLATE_FS("GPU", "Saving screenshot to '{}'."), Path::GetFileName(path)),
|
fmt::format(TRANSLATE_FS("GPU", "Saving screenshot to '{}'."), Path::GetFileName(path)),
|
||||||
60.0f);
|
60.0f);
|
||||||
|
@ -58,7 +58,8 @@ public:
|
|||||||
static std::unique_ptr<GPUBackend> CreateSoftwareBackend(GPUPresenter& presenter);
|
static std::unique_ptr<GPUBackend> CreateSoftwareBackend(GPUPresenter& presenter);
|
||||||
static std::unique_ptr<GPUBackend> CreateNullBackend(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,
|
static void RenderScreenshotToFile(const std::string_view path, DisplayScreenshotMode mode, u8 quality,
|
||||||
bool show_osd_message);
|
bool show_osd_message);
|
||||||
|
|
||||||
|
@ -746,8 +746,7 @@ u32 GPU_HW::CalculateResolutionScale() const
|
|||||||
{
|
{
|
||||||
GSVector4i display_rect, draw_rect;
|
GSVector4i display_rect, draw_rect;
|
||||||
m_presenter.CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(),
|
m_presenter.CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(),
|
||||||
g_gpu_device->GetMainSwapChain()->GetHeight(), true, true, &display_rect,
|
g_gpu_device->GetMainSwapChain()->GetHeight(), true, &display_rect, &draw_rect);
|
||||||
&draw_rect);
|
|
||||||
|
|
||||||
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
|
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
|
||||||
// anamorphic aspect ratio.
|
// 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;
|
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();
|
if (m_display_texture)
|
||||||
u32 display_area_height = g_gpu_device->GetMainSwapChain()->GetHeight();
|
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_display_rect = GSVector4i::zero();
|
||||||
GSVector4i overlay_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),
|
overlay_rect = GSVector4i::rfit(GSVector4i::loadh(target_size), m_border_overlay_texture->GetSizeVec());
|
||||||
m_border_overlay_texture->GetSizeVec());
|
|
||||||
|
|
||||||
const GSVector2 scale = GSVector2(overlay_rect.rsize()) / GSVector2(m_border_overlay_texture->GetSizeVec());
|
const GSVector2 scale = GSVector2(overlay_rect.rsize()) / GSVector2(m_border_overlay_texture->GetSizeVec());
|
||||||
overlay_display_rect =
|
overlay_display_rect =
|
||||||
GSVector4i(GSVector4(m_border_overlay_display_rect) * GSVector4::xyxy(scale)).add32(overlay_rect.xyxy());
|
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.
|
// 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 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
|
// If pre-rotation is enabled with post-processing, we need to draw to an intermediate buffer, and apply the
|
||||||
// rotation at the end.
|
// 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 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 &&
|
const bool really_postfx =
|
||||||
m_display_postfx->CheckTargets(m_present_format, postfx_size.x, postfx_size.y));
|
(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(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);
|
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 =
|
const bool apply_aspect_ratio =
|
||||||
(g_gpu_settings.display_screenshot_mode != DisplayScreenshotMode::UncorrectedInternalResolution);
|
(g_gpu_settings.display_screenshot_mode != DisplayScreenshotMode::UncorrectedInternalResolution);
|
||||||
const bool postfx = (g_gpu_settings.display_screenshot_mode != DisplayScreenshotMode::InternalResolution);
|
const bool postfx =
|
||||||
GSVector4i display_rect, draw_rect;
|
(g_gpu_settings.display_screenshot_mode == DisplayScreenshotMode::ScreenResolution &&
|
||||||
CalculateDrawRect(target->GetWidth(), target->GetHeight(), !g_gpu_settings.gpu_show_vram, apply_aspect_ratio,
|
g_gpu_device->HasMainSwapChain() && target->GetSizeVec().eq(g_gpu_device->GetMainSwapChain()->GetSizeVec()));
|
||||||
&display_rect, &draw_rect);
|
|
||||||
|
|
||||||
// Not cleared by RenderDisplay().
|
if (RenderDisplay(target, target->GetSizeVec(), postfx, apply_aspect_ratio) != GPUDevice::PresentResult::OK ||
|
||||||
g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR);
|
|
||||||
|
|
||||||
if (RenderDisplay(target, GSVector4i::zero(), GSVector4i::zero(), display_rect, draw_rect, postfx) !=
|
|
||||||
GPUDevice::PresentResult::OK ||
|
|
||||||
!cap->DeliverVideoFrame(target)) [[unlikely]]
|
!cap->DeliverVideoFrame(target)) [[unlikely]]
|
||||||
{
|
{
|
||||||
WARNING_LOG("Failed to render/deliver video capture frame.");
|
WARNING_LOG("Failed to render/deliver video capture frame.");
|
||||||
@ -907,7 +897,7 @@ bool GPUPresenter::ApplyChromaSmoothing()
|
|||||||
return true;
|
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
|
GSVector4i* display_rect, GSVector4i* draw_rect) const
|
||||||
{
|
{
|
||||||
const bool integer_scale = (g_gpu_settings.display_scaling == DisplayScalingMode::NearestInteger ||
|
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 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_width = show_vram ? VRAM_WIDTH : m_display_vram_width;
|
||||||
const u32 display_vram_height = show_vram ? VRAM_HEIGHT : m_display_vram_height;
|
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,
|
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_alignment, display_pixel_aspect_ratio,
|
||||||
g_gpu_settings.display_stretch_vertically, integer_scale, display_rect, draw_rect);
|
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();
|
ImGuiManager::RenderDebugWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
|
||||||
const GPUDevice::PresentResult pres =
|
const GPUDevice::PresentResult pres =
|
||||||
skip_present ?
|
skip_present ? GPUDevice::PresentResult::SkipPresent :
|
||||||
GPUDevice::PresentResult::SkipPresent :
|
(presenter ? presenter->RenderDisplay(nullptr, swap_chain->GetSizeVec(), true, true) :
|
||||||
(presenter ? presenter->PresentDisplay() : g_gpu_device->BeginPresent(g_gpu_device->GetMainSwapChain()));
|
g_gpu_device->BeginPresent(swap_chain));
|
||||||
if (pres == GPUDevice::PresentResult::OK)
|
if (pres == GPUDevice::PresentResult::OK)
|
||||||
{
|
{
|
||||||
if (presenter)
|
if (presenter)
|
||||||
presenter->m_skipped_present_count = 0;
|
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 GPUDevice::Features features = g_gpu_device->GetFeatures();
|
||||||
const bool scheduled_present = (present_time != 0);
|
const bool scheduled_present = (present_time != 0);
|
||||||
@ -977,7 +969,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
|||||||
SleepUntilPresentTime(present_time);
|
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())
|
if (g_gpu_device->IsGPUTimingEnabled())
|
||||||
PerformanceCounters::AccumulateGPUTime();
|
PerformanceCounters::AccumulateGPUTime();
|
||||||
@ -985,7 +977,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
|||||||
if (explicit_present)
|
if (explicit_present)
|
||||||
{
|
{
|
||||||
SleepUntilPresentTime(present_time);
|
SleepUntilPresentTime(present_time);
|
||||||
g_gpu_device->SubmitPresent(g_gpu_device->GetMainSwapChain());
|
g_gpu_device->SubmitPresent(swap_chain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1029,8 +1021,8 @@ void GPUPresenter::SleepUntilPresentTime(u64 present_time)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect,
|
bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio,
|
||||||
const GSVector4i draw_rect, bool postfx, Image* out_image)
|
Image* out_image, Error* error)
|
||||||
{
|
{
|
||||||
const ImageFormat image_format = GPUTexture::GetImageFormatForTextureFormat(m_present_format);
|
const ImageFormat image_format = GPUTexture::GetImageFormatForTextureFormat(m_present_format);
|
||||||
if (image_format == ImageFormat::None)
|
if (image_format == ImageFormat::None)
|
||||||
@ -1042,24 +1034,26 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVecto
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
g_gpu_device->ClearRenderTarget(render_texture.get(), GPUDevice::DEFAULT_CLEAR_COLOR);
|
g_gpu_device->ClearRenderTarget(render_texture.get(), GPUDevice::DEFAULT_CLEAR_COLOR);
|
||||||
|
if (RenderDisplay(render_texture.get(), render_texture->GetSizeVec(), postfx, apply_aspect_ratio) !=
|
||||||
// TODO: this should use copy shader instead.
|
GPUDevice::PresentResult::OK)
|
||||||
RenderDisplay(render_texture.get(), GSVector4i::zero(), GSVector4i::zero(), display_rect, draw_rect, postfx);
|
{
|
||||||
|
Error::SetStringView(error, "RenderDisplay() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Image image(width, height, image_format);
|
Image image(width, height, image_format);
|
||||||
|
|
||||||
Error error;
|
|
||||||
std::unique_ptr<GPUDownloadTexture> dltex;
|
std::unique_ptr<GPUDownloadTexture> dltex;
|
||||||
if (g_gpu_device->GetFeatures().memory_import)
|
if (g_gpu_device->GetFeatures().memory_import)
|
||||||
{
|
{
|
||||||
dltex = g_gpu_device->CreateDownloadTexture(width, height, m_present_format, image.GetPixels(),
|
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)
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1072,8 +1066,7 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVecto
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height,
|
GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) const
|
||||||
GSVector4i* display_rect, GSVector4i* draw_rect) const
|
|
||||||
{
|
{
|
||||||
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution || g_gpu_settings.gpu_show_vram);
|
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)
|
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);
|
f_width = f_width / (f_height / max_texture_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
*width = static_cast<u32>(std::ceil(f_width));
|
return GSVector2i(static_cast<s32>(std::ceil(f_width)), static_cast<s32>(std::ceil(f_height)));
|
||||||
*height = static_cast<u32>(std::ceil(f_height));
|
|
||||||
}
|
}
|
||||||
else // if (mode == DisplayScreenshotMode::UncorrectedInternalResolution)
|
else // if (mode == DisplayScreenshotMode::UncorrectedInternalResolution)
|
||||||
{
|
{
|
||||||
*width = m_display_texture_view_width;
|
return GSVector2i(m_display_texture_view_width, m_display_texture_view_height);
|
||||||
*height = 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
|
else
|
||||||
{
|
{
|
||||||
*width = g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetWidth() : 1;
|
return g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetSizeVec() : GSVector2i(1, 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ public:
|
|||||||
ALWAYS_INLINE s32 GetDisplayTextureViewHeight() const { return m_display_texture_view_height; }
|
ALWAYS_INLINE s32 GetDisplayTextureViewHeight() const { return m_display_texture_view_height; }
|
||||||
ALWAYS_INLINE GPUTexture* GetDisplayTexture() const { return m_display_texture; }
|
ALWAYS_INLINE GPUTexture* GetDisplayTexture() const { return m_display_texture; }
|
||||||
ALWAYS_INLINE bool HasDisplayTexture() 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);
|
bool Initialize(Error* error);
|
||||||
|
|
||||||
@ -59,16 +60,15 @@ public:
|
|||||||
bool ApplyChromaSmoothing();
|
bool ApplyChromaSmoothing();
|
||||||
|
|
||||||
/// Helper function for computing the draw rectangle in a larger window.
|
/// 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,
|
void CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio, GSVector4i* display_rect,
|
||||||
GSVector4i* display_rect, GSVector4i* draw_rect) const;
|
GSVector4i* draw_rect) const;
|
||||||
|
|
||||||
/// Helper function for computing screenshot bounds.
|
/// Helper function for computing screenshot bounds.
|
||||||
void CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height, GSVector4i* display_rect,
|
GSVector2i CalculateScreenshotSize(DisplayScreenshotMode mode) const;
|
||||||
GSVector4i* draw_rect) const;
|
|
||||||
|
|
||||||
/// Renders the display, optionally with postprocessing to the specified image.
|
/// 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 RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio, Image* out_image,
|
||||||
bool postfx, Image* out_image);
|
Error* error);
|
||||||
|
|
||||||
/// Sends the current frame to media capture.
|
/// Sends the current frame to media capture.
|
||||||
void SendDisplayToMediaCapture(MediaCapture* cap);
|
void SendDisplayToMediaCapture(MediaCapture* cap);
|
||||||
@ -98,14 +98,10 @@ private:
|
|||||||
|
|
||||||
static void SleepUntilPresentTime(u64 present_time);
|
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);
|
bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error);
|
||||||
|
|
||||||
GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect,
|
GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector2i target_size, bool postfx,
|
||||||
const GSVector4i overlay_display_rect, const GSVector4i display_rect,
|
bool apply_aspect_ratio);
|
||||||
const GSVector4i draw_rect, bool postfx);
|
|
||||||
|
|
||||||
void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend,
|
void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend,
|
||||||
DisplayRotation rotation, WindowInfo::PreRotation prerotation);
|
DisplayRotation rotation, WindowInfo::PreRotation prerotation);
|
||||||
|
@ -3257,7 +3257,9 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
|
|||||||
// save screenshot
|
// save screenshot
|
||||||
if (screenshot_size > 0)
|
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())
|
if (g_gpu_device->UsesLowerLeftOrigin())
|
||||||
buffer->screenshot.FlipY();
|
buffer->screenshot.FlipY();
|
||||||
@ -3265,12 +3267,11 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
|
|||||||
// Ensure it's RGBA8.
|
// Ensure it's RGBA8.
|
||||||
if (buffer->screenshot.GetFormat() != ImageFormat::RGBA8)
|
if (buffer->screenshot.GetFormat() != ImageFormat::RGBA8)
|
||||||
{
|
{
|
||||||
Error convert_error;
|
std::optional<Image> screenshot_rgba8 = buffer->screenshot.ConvertToRGBA8(&screenshot_error);
|
||||||
std::optional<Image> screenshot_rgba8 = buffer->screenshot.ConvertToRGBA8(&convert_error);
|
|
||||||
if (!screenshot_rgba8.has_value())
|
if (!screenshot_rgba8.has_value())
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to convert {} screenshot to RGBA8: {}",
|
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();
|
buffer->screenshot.Invalidate();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -3281,8 +3282,8 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WARNING_LOG("Failed to save {}x{} screenshot for save state due to render/conversion failure", screenshot_size,
|
WARNING_LOG("Failed to save {}x{} screenshot for save state: {}", screenshot_size, 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
|
// need to query this on the GPU thread
|
||||||
GPUThread::RunOnBackend(
|
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)
|
if (!backend)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GSVector4i unused_display_rect, unused_draw_rect;
|
// Prefer aligning for non-window size.
|
||||||
u32 video_width, video_height;
|
const GSVector2i video_size = backend->GetPresenter().CalculateScreenshotSize(mode);
|
||||||
backend->GetPresenter().CalculateScreenshotSize(DisplayScreenshotMode::InternalResolution, &video_width,
|
u32 video_width = static_cast<u32>(video_size.x);
|
||||||
&video_height, &unused_display_rect, &unused_draw_rect);
|
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
|
// fire back to the CPU thread to actually start the capture
|
||||||
Host::RunOnCPUThread([path = std::move(path), capture_audio, video_width, video_height]() mutable {
|
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);
|
Host::GetUIntSettingValue("MediaCapture", "VideoWidth", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH);
|
||||||
u32 video_height =
|
u32 video_height =
|
||||||
Host::GetUIntSettingValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_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);
|
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 WindowInfo& main_window_info = GPUThread::GetRenderWindowInfo();
|
||||||
const GPUTexture::Format capture_format =
|
const GPUTexture::Format capture_format =
|
||||||
main_window_info.IsSurfaceless() ? GPUTexture::Format::RGBA8 : main_window_info.surface_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?
|
// TODO: Render anamorphic capture instead?
|
||||||
constexpr float aspect = 1.0f;
|
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.");
|
Error::SetStringView(error, "No path specified.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (capture_video &&
|
else if (capture_video && (fps == 0.0f || m_video_width == 0 || m_video_height == 0))
|
||||||
(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)))
|
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Invalid video dimensions/rate.");
|
Error::SetStringView(error, "Invalid video dimensions/rate.");
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user