diff options
| author | Liam <byteslice@airmail.cc> | 2024-01-26 16:10:21 -0500 | 
|---|---|---|
| committer | Liam <byteslice@airmail.cc> | 2024-02-09 09:20:53 -0500 | 
| commit | 0cb413c3d31b93ce347e997b9674c304094dab09 (patch) | |
| tree | 959ef73963ea1e7975690d31eaf4ddc2f2ad6e19 | |
| parent | 962c82540c304f909957776908aabcd261f2a7ba (diff) | |
nvnflinger/gpu: implement applet capture
19 files changed, 288 insertions, 55 deletions
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp index 3cccc5388..7fb9e3a75 100644 --- a/src/core/hle/service/am/system_buffer_manager.cpp +++ b/src/core/hle/service/am/system_buffer_manager.cpp @@ -68,8 +68,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) {  Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,                                                       s32* out_fbshare_layer_index) { -    // TODO -    R_SUCCEED(); +    if (!m_buffer_sharing_enabled) { +        return VI::ResultPermissionDenied; +    } + +    return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written, +                                                                           out_fbshare_layer_index);  }  } // namespace Service::AM diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index 6a7da0cae..90f7248a0 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp @@ -15,6 +15,7 @@  #include "core/hle/service/vi/layer/vi_layer.h"  #include "core/hle/service/vi/vi_results.h"  #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h"  namespace Service::Nvnflinger { @@ -414,9 +415,30 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab      R_SUCCEED();  } -Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, -                                                      s32* out_layer_index) { -    // TODO +Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { +    std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer()); +    Common::ScratchBuffer<u32> scratch; + +    // TODO: this could be optimized +    s64 e = -1280 * 768 * 4; +    for (auto& block : *m_buffer_page_group) { +        u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress()); +        u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize()); + +        for (; start < end; start++) { +            *start = 0; + +            if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) { +                *start = capture_buffer[e]; +            } +            e++; +        } + +        m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) { +            m_system.GPU().InvalidateRegion(addr, end - start); +        }); +    } +      *out_was_written = true;      *out_layer_index = 1;      R_SUCCEED(); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 55180f4b5..dbed976ad 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(video_core STATIC      buffer_cache/usage_tracker.h      buffer_cache/word_manager.h      cache_types.h +    capture.h      cdma_pusher.cpp      cdma_pusher.h      compatible_formats.cpp diff --git a/src/video_core/capture.h b/src/video_core/capture.h new file mode 100644 index 000000000..8db14a8ec --- /dev/null +++ b/src/video_core/capture.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/bit_util.h" +#include "common/common_types.h" +#include "core/frontend/framebuffer_layout.h" +#include "video_core/surface.h" + +namespace VideoCore::Capture { + +constexpr u32 BlockHeight = 4; +constexpr u32 BlockDepth = 0; +constexpr u32 BppLog2 = 2; + +constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM; + +constexpr auto LinearWidth = Layout::ScreenUndocked::Width; +constexpr auto LinearHeight = Layout::ScreenUndocked::Height; +constexpr auto LinearDepth = 1U; +constexpr auto BytesPerPixel = 4U; + +constexpr auto TiledWidth = LinearWidth; +constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2); +constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2); + +constexpr Layout::FramebufferLayout Layout{ +    .width = LinearWidth, +    .height = LinearHeight, +    .screen = {0, 0, LinearWidth, LinearHeight}, +    .is_srgb = false, +}; + +} // namespace VideoCore::Capture diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index f4a5d831c..8e663f2a8 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -347,6 +347,17 @@ struct GPU::Impl {          WaitForSyncOperation(wait_fence);      } +    std::vector<u8> GetAppletCaptureBuffer() { +        std::vector<u8> out; + +        const auto wait_fence = +            RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); }); +        gpu_thread.TickGPU(); +        WaitForSyncOperation(wait_fence); + +        return out; +    } +      GPU& gpu;      Core::System& system;      Host1x::Host1x& host1x; @@ -505,6 +516,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,      impl->RequestComposite(std::move(layers), std::move(fences));  } +std::vector<u8> GPU::GetAppletCaptureBuffer() { +    return impl->GetAppletCaptureBuffer(); +} +  u64 GPU::GetTicks() const {      return impl->GetTicks();  } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index c4602ca37..ad535512c 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -215,6 +215,8 @@ public:      void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,                            std::vector<Service::Nvidia::NvFence>&& fences); +    std::vector<u8> GetAppletCaptureBuffer(); +      /// Performs any additional setup necessary in order to begin GPU emulation.      /// This can be used to launch any necessary threads and register any necessary      /// core timing events. diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 3ad180f67..67427f937 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -40,6 +40,9 @@ public:      /// Finalize rendering the guest frame and draw into the presentation texture      virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; +    /// Get the tiled applet layer capture buffer +    virtual std::vector<u8> GetAppletCaptureBuffer() = 0; +      [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;      [[nodiscard]] virtual std::string GetDeviceVendor() const = 0; diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp index c89daff53..e6147d66c 100644 --- a/src/video_core/renderer_null/renderer_null.cpp +++ b/src/video_core/renderer_null/renderer_null.cpp @@ -3,6 +3,7 @@  #include "core/frontend/emu_window.h"  #include "core/frontend/graphics_context.h" +#include "video_core/capture.h"  #include "video_core/renderer_null/renderer_null.h"  namespace Null { @@ -22,4 +23,8 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff      render_window.OnFrameDisplayed();  } +std::vector<u8> RendererNull::GetAppletCaptureBuffer() { +    return std::vector<u8>(VideoCore::Capture::TiledSize); +} +  } // namespace Null diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h index 063b476bb..34dbe1e4f 100644 --- a/src/video_core/renderer_null/renderer_null.h +++ b/src/video_core/renderer_null/renderer_null.h @@ -19,6 +19,8 @@ public:      void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; +    std::vector<u8> GetAppletCaptureBuffer() override; +      VideoCore::RasterizerInterface* ReadRasterizer() override {          return &m_rasterizer;      } diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 6ba8b214b..0282905ee 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -20,7 +20,7 @@ BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,  BlitScreen::~BlitScreen() = default;  void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, -                            const Layout::FramebufferLayout& layout) { +                            const Layout::FramebufferLayout& layout, bool invert_y) {      // TODO: Signal state tracker about these changes      state_tracker.NotifyScreenDrawVertexArray();      state_tracker.NotifyPolygonModes(); @@ -60,7 +60,7 @@ void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffe      }      CreateWindowAdapt(); -    window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); +    window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout, invert_y);      // TODO      // program_manager.RestoreGuestPipeline(); diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h index 0c3d838f1..4e261d333 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.h +++ b/src/video_core/renderer_opengl/gl_blit_screen.h @@ -51,7 +51,7 @@ public:      /// Draws the emulated screens to the emulator window.      void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, -                    const Layout::FramebufferLayout& layout); +                    const Layout::FramebufferLayout& layout, bool invert_y);  private:      void CreateWindowAdapt(); diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp index e3fffaa06..3e4377db4 100644 --- a/src/video_core/renderer_opengl/present/layer.cpp +++ b/src/video_core/renderer_opengl/present/layer.cpp @@ -34,7 +34,7 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,                              std::array<ScreenRectVertex, 4>& out_vertices,                              ProgramManager& program_manager,                              const Tegra::FramebufferConfig& framebuffer, -                            const Layout::FramebufferLayout& layout) { +                            const Layout::FramebufferLayout& layout, bool invert_y) {      FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);      auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);      GLuint texture = info.display_texture; @@ -83,10 +83,15 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,      const auto w = screen.GetWidth();      const auto h = screen.GetHeight(); -    out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); -    out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); -    out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); -    out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); +    const auto left = crop.left; +    const auto right = crop.right; +    const auto top = invert_y ? crop.bottom : crop.top; +    const auto bottom = invert_y ? crop.top : crop.bottom; + +    out_vertices[0] = ScreenRectVertex(x, y, left, top); +    out_vertices[1] = ScreenRectVertex(x + w, y, right, top); +    out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom); +    out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);      return texture;  } diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h index ef1055abf..77bb97f4f 100644 --- a/src/video_core/renderer_opengl/present/layer.h +++ b/src/video_core/renderer_opengl/present/layer.h @@ -51,7 +51,7 @@ public:                           std::array<ScreenRectVertex, 4>& out_vertices,                           ProgramManager& program_manager,                           const Tegra::FramebufferConfig& framebuffer, -                         const Layout::FramebufferLayout& layout); +                         const Layout::FramebufferLayout& layout, bool invert_y);  private:      /// Loads framebuffer from emulated memory into the active OpenGL texture. diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp index 0328abd70..d8b6a11cb 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp @@ -37,7 +37,7 @@ WindowAdaptPass::~WindowAdaptPass() = default;  void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,                                          std::span<const Tegra::FramebufferConfig> framebuffers, -                                        const Layout::FramebufferLayout& layout) { +                                        const Layout::FramebufferLayout& layout, bool invert_y) {      GLint old_read_fb;      GLint old_draw_fb;      glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); @@ -51,7 +51,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li      auto layer_it = layers.begin();      for (size_t i = 0; i < layer_count; i++) {          textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, -                                              framebuffers[i], layout); +                                              framebuffers[i], layout, invert_y);          layer_it++;      } diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h index 00975a9c6..0a8bcef2f 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.h +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h @@ -31,7 +31,7 @@ public:      void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,                             std::span<const Tegra::FramebufferConfig> framebuffers, -                           const Layout::FramebufferLayout& layout); +                           const Layout::FramebufferLayout& layout, bool invert_y);  private:      const Device& device; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e33a32592..a1a8491e5 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -16,6 +16,7 @@  #include "core/core_timing.h"  #include "core/frontend/emu_window.h"  #include "core/telemetry_session.h" +#include "video_core/capture.h"  #include "video_core/renderer_opengl/gl_blit_screen.h"  #include "video_core/renderer_opengl/gl_rasterizer.h"  #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -121,6 +122,13 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,      }      blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,                                                 program_manager, device); +    blit_applet = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, +                                               program_manager, device); +    capture_framebuffer.Create(); +    capture_renderbuffer.Create(); +    glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle); +    glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth, +                          VideoCore::Capture::LinearHeight);  }  RendererOpenGL::~RendererOpenGL() = default; @@ -130,10 +138,11 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu          return;      } +    RenderAppletCaptureLayer(framebuffers);      RenderScreenshot(framebuffers);      state_tracker.BindFramebuffer(0); -    blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); +    blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false);      ++m_current_frame; @@ -159,11 +168,8 @@ void RendererOpenGL::AddTelemetryFields() {      telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));  } -void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { -    if (!renderer_settings.screenshot_requested) { -        return; -    } - +void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, +                                    const Layout::FramebufferLayout& layout, void* dst) {      GLint old_read_fb;      GLint old_draw_fb;      glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); @@ -173,29 +179,86 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>      screenshot_framebuffer.Create();      glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); -    const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; -      GLuint renderbuffer;      glGenRenderbuffers(1, &renderbuffer);      glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);      glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); -    blit_screen->DrawScreen(framebuffers, layout); +    blit_screen->DrawScreen(framebuffers, layout, false);      glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);      glPixelStorei(GL_PACK_ROW_LENGTH, 0); -    glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, -                 renderer_settings.screenshot_bits); +    glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst);      screenshot_framebuffer.Release();      glDeleteRenderbuffers(1, &renderbuffer);      glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);      glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); +} + +void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { +    if (!renderer_settings.screenshot_requested) { +        return; +    } + +    RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout, +                   renderer_settings.screenshot_bits);      renderer_settings.screenshot_complete_callback(true);      renderer_settings.screenshot_requested = false;  } +void RendererOpenGL::RenderAppletCaptureLayer( +    std::span<const Tegra::FramebufferConfig> framebuffers) { +    GLint old_read_fb; +    GLint old_draw_fb; +    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); +    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + +    glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle); +    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, +                              capture_renderbuffer.handle); + +    blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true); + +    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); +    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); +} + +std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() { +    using namespace VideoCore::Capture; + +    std::vector<u8> linear(TiledSize); +    std::vector<u8> out(TiledSize); + +    GLint old_read_fb; +    GLint old_draw_fb; +    GLint old_pixel_pack_buffer; +    GLint old_pack_row_length; +    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); +    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); +    glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer); +    glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length); + +    glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle); +    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, +                              capture_renderbuffer.handle); +    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +    glPixelStorei(GL_PACK_ROW_LENGTH, 0); +    glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, +                 linear.data()); + +    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); +    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); +    glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer); +    glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length); + +    Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight, +                                   LinearDepth, BlockHeight, BlockDepth); + +    return out; +} +  } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c4625c96e..60d6a1477 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -42,6 +42,8 @@ public:      void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; +    std::vector<u8> GetAppletCaptureBuffer() override; +      VideoCore::RasterizerInterface* ReadRasterizer() override {          return &rasterizer;      } @@ -52,7 +54,11 @@ public:  private:      void AddTelemetryFields(); + +    void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, +                        const Layout::FramebufferLayout& layout, void* dst);      void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); +    void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);      Core::TelemetrySession& telemetry_session;      Core::Frontend::EmuWindow& emu_window; @@ -64,8 +70,11 @@ private:      ProgramManager program_manager;      RasterizerOpenGL rasterizer;      OGLFramebuffer screenshot_framebuffer; +    OGLFramebuffer capture_framebuffer; +    OGLRenderbuffer capture_renderbuffer;      std::unique_ptr<BlitScreen> blit_screen; +    std::unique_ptr<BlitScreen> blit_applet;  };  } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index c7c234fd8..c148efef2 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -19,6 +19,7 @@  #include "core/core_timing.h"  #include "core/frontend/graphics_context.h"  #include "core/telemetry_session.h" +#include "video_core/capture.h"  #include "video_core/gpu.h"  #include "video_core/renderer_vulkan/present/util.h"  #include "video_core/renderer_vulkan/renderer_vulkan.h" @@ -38,6 +39,20 @@  namespace Vulkan {  namespace { + +constexpr VkExtent2D CaptureImageSize{ +    .width = VideoCore::Capture::LinearWidth, +    .height = VideoCore::Capture::LinearHeight, +}; + +constexpr VkExtent3D CaptureImageExtent{ +    .width = VideoCore::Capture::LinearWidth, +    .height = VideoCore::Capture::LinearHeight, +    .depth = VideoCore::Capture::LinearDepth, +}; + +constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; +  std::string GetReadableVersion(u32 version) {      return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),                         VK_VERSION_PATCH(version)); @@ -100,11 +115,11 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,        present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,                        surface),        blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler), -      blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler), -      blit_application_layer(device_memory, device, memory_allocator, present_manager, scheduler), +      blit_capture(device_memory, device, memory_allocator, present_manager, scheduler), +      blit_applet(device_memory, device, memory_allocator, present_manager, scheduler),        rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,                   scheduler), -      application_frame() { +      applet_frame() {      if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {          turbo_mode.emplace(instance, dld);          scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); @@ -127,6 +142,8 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu      SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); +    RenderAppletCaptureLayer(framebuffers); +      if (!render_window.IsShown()) {          return;      } @@ -169,30 +186,20 @@ void RendererVulkan::Report() const {      telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);  } -void Vulkan::RendererVulkan::RenderScreenshot( -    std::span<const Tegra::FramebufferConfig> framebuffers) { -    if (!renderer_settings.screenshot_requested) { -        return; -    } - -    constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; -    const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; - +vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, +                                          const Layout::FramebufferLayout& layout, VkFormat format, +                                          VkDeviceSize buffer_size) {      auto frame = [&]() {          Frame f{}; -        f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, -                                     ScreenshotFormat); -        f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); -        f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); +        f.image = +            CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, format); +        f.image_view = CreateWrappedImageView(device, f.image, format); +        f.framebuffer = blit_capture.CreateFramebuffer(layout, *f.image_view, format);          return f;      }(); -    blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, -                                VK_FORMAT_B8G8R8A8_UNORM); - -    const auto dst_buffer = CreateWrappedBuffer( -        memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), -        MemoryUsage::Download); +    auto dst_buffer = CreateWrappedBuffer(memory_allocator, buffer_size, MemoryUsage::Download); +    blit_capture.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, format);      scheduler.RequestOutsideRenderPassOperationContext();      scheduler.Record([&](vk::CommandBuffer cmdbuf) { @@ -200,15 +207,68 @@ void Vulkan::RendererVulkan::RenderScreenshot(                             VkExtent3D{layout.width, layout.height, 1});      }); -    // Ensure the copy is fully completed before saving the screenshot +    // Ensure the copy is fully completed before saving the capture      scheduler.Finish(); -    // Copy backing image data to the QImage screenshot buffer +    // Copy backing image data to the capture buffer      dst_buffer.Invalidate(); +    return dst_buffer; +} + +void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { +    if (!renderer_settings.screenshot_requested) { +        return; +    } + +    const auto& layout{renderer_settings.screenshot_framebuffer_layout}; +    const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM, +                                           layout.width * layout.height * 4); +      std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),                  dst_buffer.Mapped().size());      renderer_settings.screenshot_complete_callback(false);      renderer_settings.screenshot_requested = false;  } +std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() { +    using namespace VideoCore::Capture; + +    std::vector<u8> out(VideoCore::Capture::TiledSize); + +    if (!applet_frame.image) { +        return out; +    } + +    const auto dst_buffer = +        CreateWrappedBuffer(memory_allocator, VideoCore::Capture::TiledSize, MemoryUsage::Download); + +    scheduler.RequestOutsideRenderPassOperationContext(); +    scheduler.Record([&](vk::CommandBuffer cmdbuf) { +        DownloadColorImage(cmdbuf, *applet_frame.image, *dst_buffer, CaptureImageExtent); +    }); + +    // Ensure the copy is fully completed before writing the capture +    scheduler.Finish(); + +    // Swizzle image data to the capture buffer +    dst_buffer.Invalidate(); +    Tegra::Texture::SwizzleTexture(out, dst_buffer.Mapped(), BytesPerPixel, LinearWidth, +                                   LinearHeight, LinearDepth, BlockHeight, BlockDepth); + +    return out; +} + +void RendererVulkan::RenderAppletCaptureLayer( +    std::span<const Tegra::FramebufferConfig> framebuffers) { +    if (!applet_frame.image) { +        applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat); +        applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat); +        applet_frame.framebuffer = blit_applet.CreateFramebuffer( +            VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat); +    } + +    blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1, +                            CaptureFormat); +} +  } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index ed9c7af7f..fb9d83412 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -48,6 +48,8 @@ public:      void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; +    std::vector<u8> GetAppletCaptureBuffer() override; +      VideoCore::RasterizerInterface* ReadRasterizer() override {          return &rasterizer;      } @@ -59,7 +61,11 @@ public:  private:      void Report() const; +    vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, +                              const Layout::FramebufferLayout& layout, VkFormat format, +                              VkDeviceSize buffer_size);      void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); +    void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);      Core::TelemetrySession& telemetry_session;      Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -79,12 +85,12 @@ private:      Swapchain swapchain;      PresentManager present_manager;      BlitScreen blit_swapchain; -    BlitScreen blit_screenshot; -    BlitScreen blit_application_layer; +    BlitScreen blit_capture; +    BlitScreen blit_applet;      RasterizerVulkan rasterizer;      std::optional<TurboMode> turbo_mode; -    Frame application_frame; +    Frame applet_frame;  };  } // namespace Vulkan  | 
