diff --git a/src/core/gpu_backend.cpp b/src/core/gpu_backend.cpp index 58d0f9e53..b6c20c862 100644 --- a/src/core/gpu_backend.cpp +++ b/src/core/gpu_backend.cpp @@ -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(width), static_cast(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(width), static_cast(height), apply_aspect_ratio, + &display_rect, &draw_rect); + image_width = static_cast(display_rect.width()); + image_height = static_cast(display_rect.height()); + } - // Crop it. - const u32 cropped_width = static_cast(display_rect.width()); - const u32 cropped_height = static_cast(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); diff --git a/src/core/gpu_backend.h b/src/core/gpu_backend.h index e55edea31..07c69cf73 100644 --- a/src/core/gpu_backend.h +++ b/src/core/gpu_backend.h @@ -58,7 +58,8 @@ public: static std::unique_ptr CreateSoftwareBackend(GPUPresenter& presenter); static std::unique_ptr 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); diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 2436bd1be..d8c231f8d 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -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. diff --git a/src/core/gpu_presenter.cpp b/src/core/gpu_presenter.cpp index 462977d9f..91f7be71d 100644 --- a/src/core/gpu_presenter.cpp +++ b/src/core/gpu_presenter.cpp @@ -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(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(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(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 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(std::ceil(f_width)); - *height = static_cast(std::ceil(f_height)); + return GSVector2i(static_cast(std::ceil(f_width)), static_cast(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(*width), static_cast(*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); } } diff --git a/src/core/gpu_presenter.h b/src/core/gpu_presenter.h index 816329a74..ee4909780 100644 --- a/src/core/gpu_presenter.h +++ b/src/core/gpu_presenter.h @@ -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(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); diff --git a/src/core/system.cpp b/src/core/system.cpp index f8d6a043f..105bfb1ec 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -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 screenshot_rgba8 = buffer->screenshot.ConvertToRGBA8(&convert_error); + std::optional 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(video_size.x); + u32 video_height = static_cast(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; diff --git a/src/util/media_capture.cpp b/src/util/media_capture.cpp index af251f026..57258e4fa 100644 --- a/src/util/media_capture.cpp +++ b/src/util/media_capture.cpp @@ -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;