diff options
Diffstat (limited to 'src')
28 files changed, 469 insertions, 252 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 570acb193..eb8f643a2 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -775,6 +775,9 @@ add_library(core STATIC | |||
| 775 | hle/service/nvnflinger/graphic_buffer_producer.h | 775 | hle/service/nvnflinger/graphic_buffer_producer.h |
| 776 | hle/service/nvnflinger/hos_binder_driver_server.cpp | 776 | hle/service/nvnflinger/hos_binder_driver_server.cpp |
| 777 | hle/service/nvnflinger/hos_binder_driver_server.h | 777 | hle/service/nvnflinger/hos_binder_driver_server.h |
| 778 | hle/service/nvnflinger/hardware_composer.cpp | ||
| 779 | hle/service/nvnflinger/hardware_composer.h | ||
| 780 | hle/service/nvnflinger/hwc_layer.h | ||
| 778 | hle/service/nvnflinger/nvnflinger.cpp | 781 | hle/service/nvnflinger/nvnflinger.cpp |
| 779 | hle/service/nvnflinger/nvnflinger.h | 782 | hle/service/nvnflinger/nvnflinger.h |
| 780 | hle/service/nvnflinger/parcel.h | 783 | hle/service/nvnflinger/parcel.h |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index c1ebbd62d..abe95303e 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <boost/container/small_vector.hpp> | ||
| 5 | |||
| 4 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 5 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| @@ -38,19 +40,30 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 38 | void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} | 40 | void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} |
| 39 | void nvdisp_disp0::OnClose(DeviceFD fd) {} | 41 | void nvdisp_disp0::OnClose(DeviceFD fd) {} |
| 40 | 42 | ||
| 41 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, | 43 | void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers) { |
| 42 | u32 height, u32 stride, android::BufferTransformFlags transform, | 44 | std::vector<Tegra::FramebufferConfig> output_layers; |
| 43 | const Common::Rectangle<int>& crop_rect, | 45 | std::vector<Service::Nvidia::NvFence> output_fences; |
| 44 | std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { | 46 | output_layers.reserve(sorted_layers.size()); |
| 45 | const DAddr addr = nvmap.GetHandleAddress(buffer_handle); | 47 | output_fences.reserve(sorted_layers.size()); |
| 46 | LOG_TRACE(Service, | 48 | |
| 47 | "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", | 49 | for (auto& layer : sorted_layers) { |
| 48 | addr, offset, width, height, stride, format); | 50 | output_layers.emplace_back(Tegra::FramebufferConfig{ |
| 51 | .address = nvmap.GetHandleAddress(layer.buffer_handle), | ||
| 52 | .offset = layer.offset, | ||
| 53 | .width = layer.width, | ||
| 54 | .height = layer.height, | ||
| 55 | .stride = layer.stride, | ||
| 56 | .pixel_format = layer.format, | ||
| 57 | .transform_flags = layer.transform, | ||
| 58 | .crop_rect = layer.crop_rect, | ||
| 59 | }); | ||
| 49 | 60 | ||
| 50 | const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, | 61 | for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { |
| 51 | stride, format, transform, crop_rect}; | 62 | output_fences.push_back(layer.acquire_fence.fences[i]); |
| 63 | } | ||
| 64 | } | ||
| 52 | 65 | ||
| 53 | system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); | 66 | system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences)); |
| 54 | system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); | 67 | system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); |
| 55 | system.GetPerfStats().EndSystemFrame(); | 68 | system.GetPerfStats().EndSystemFrame(); |
| 56 | system.GetPerfStats().BeginSystemFrame(); | 69 | system.GetPerfStats().BeginSystemFrame(); |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 5f13a50a2..1082b85c2 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | |||
| @@ -8,8 +8,7 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/math_util.h" | 9 | #include "common/math_util.h" |
| 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 11 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | 11 | #include "core/hle/service/nvnflinger/hwc_layer.h" |
| 12 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 13 | 12 | ||
| 14 | namespace Service::Nvidia::NvCore { | 13 | namespace Service::Nvidia::NvCore { |
| 15 | class Container; | 14 | class Container; |
| @@ -35,11 +34,8 @@ public: | |||
| 35 | void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; | 34 | void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; |
| 36 | void OnClose(DeviceFD fd) override; | 35 | void OnClose(DeviceFD fd) override; |
| 37 | 36 | ||
| 38 | /// Performs a screen flip, drawing the buffer pointed to by the handle. | 37 | /// Performs a screen flip, compositing each buffer. |
| 39 | void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, | 38 | void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers); |
| 40 | u32 stride, android::BufferTransformFlags transform, | ||
| 41 | const Common::Rectangle<int>& crop_rect, | ||
| 42 | std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); | ||
| 43 | 39 | ||
| 44 | Kernel::KEvent* QueryEvent(u32 event_id) override; | 40 | Kernel::KEvent* QueryEvent(u32 event_id) override; |
| 45 | 41 | ||
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h index 7fd808f54..f9f262628 100644 --- a/src/core/hle/service/nvnflinger/buffer_item.h +++ b/src/core/hle/service/nvnflinger/buffer_item.h | |||
| @@ -40,7 +40,7 @@ public: | |||
| 40 | bool is_droppable{}; | 40 | bool is_droppable{}; |
| 41 | bool acquire_called{}; | 41 | bool acquire_called{}; |
| 42 | bool transform_to_display_inverse{}; | 42 | bool transform_to_display_inverse{}; |
| 43 | s32 swap_interval{}; | 43 | u32 swap_interval{}; |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | } // namespace Service::android | 46 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp new file mode 100644 index 000000000..54889bb4f --- /dev/null +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include <boost/container/small_vector.hpp> | ||
| 5 | |||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" | ||
| 8 | #include "core/hle/service/nvnflinger/buffer_item.h" | ||
| 9 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | ||
| 10 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | ||
| 11 | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||
| 12 | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||
| 13 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | ||
| 14 | #include "core/hle/service/vi/display/vi_display.h" | ||
| 15 | #include "core/hle/service/vi/layer/vi_layer.h" | ||
| 16 | |||
| 17 | namespace Service::Nvnflinger { | ||
| 18 | |||
| 19 | HardwareComposer::HardwareComposer() = default; | ||
| 20 | HardwareComposer::~HardwareComposer() = default; | ||
| 21 | |||
| 22 | u32 HardwareComposer::ComposeLocked(VI::Display& display, Nvidia::Devices::nvdisp_disp0& nvdisp, | ||
| 23 | u32 frame_advance) { | ||
| 24 | boost::container::small_vector<HwcLayer, 2> composition_stack; | ||
| 25 | |||
| 26 | m_frame_number += frame_advance; | ||
| 27 | |||
| 28 | // Release any necessary framebuffers. | ||
| 29 | for (auto& [layer_id, framebuffer] : m_framebuffers) { | ||
| 30 | if (framebuffer.release_frame_number > m_frame_number) { | ||
| 31 | // Not yet ready to release this framebuffer. | ||
| 32 | continue; | ||
| 33 | } | ||
| 34 | |||
| 35 | if (!framebuffer.is_acquired) { | ||
| 36 | // Already released. | ||
| 37 | continue; | ||
| 38 | } | ||
| 39 | |||
| 40 | if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { | ||
| 41 | // TODO: support release fence | ||
| 42 | // This is needed to prevent screen tearing | ||
| 43 | layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); | ||
| 44 | framebuffer.is_acquired = false; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | // Determine the number of vsync periods to wait before composing again. | ||
| 49 | std::optional<u32> swap_interval{}; | ||
| 50 | bool has_acquired_buffer{}; | ||
| 51 | |||
| 52 | // Acquire all necessary framebuffers. | ||
| 53 | for (size_t i = 0; i < display.GetNumLayers(); i++) { | ||
| 54 | auto& layer = display.GetLayer(i); | ||
| 55 | auto layer_id = layer.GetLayerId(); | ||
| 56 | |||
| 57 | // Try to fetch the framebuffer (either new or stale). | ||
| 58 | const auto result = this->CacheFramebufferLocked(layer, layer_id); | ||
| 59 | |||
| 60 | // If we failed, skip this layer. | ||
| 61 | if (result == CacheStatus::NoBufferAvailable) { | ||
| 62 | continue; | ||
| 63 | } | ||
| 64 | |||
| 65 | // If we acquired a new buffer, we need to present. | ||
| 66 | if (result == CacheStatus::BufferAcquired) { | ||
| 67 | has_acquired_buffer = true; | ||
| 68 | } | ||
| 69 | |||
| 70 | const auto& buffer = m_framebuffers[layer_id]; | ||
| 71 | const auto& item = buffer.item; | ||
| 72 | const auto& igbp_buffer = *item.graphic_buffer; | ||
| 73 | |||
| 74 | // TODO: get proper Z-index from layer | ||
| 75 | composition_stack.emplace_back(HwcLayer{ | ||
| 76 | .buffer_handle = igbp_buffer.BufferId(), | ||
| 77 | .offset = igbp_buffer.Offset(), | ||
| 78 | .format = igbp_buffer.ExternalFormat(), | ||
| 79 | .width = igbp_buffer.Width(), | ||
| 80 | .height = igbp_buffer.Height(), | ||
| 81 | .stride = igbp_buffer.Stride(), | ||
| 82 | .z_index = 0, | ||
| 83 | .transform = static_cast<android::BufferTransformFlags>(item.transform), | ||
| 84 | .crop_rect = item.crop, | ||
| 85 | .acquire_fence = item.fence, | ||
| 86 | }); | ||
| 87 | |||
| 88 | // We need to compose again either before this frame is supposed to | ||
| 89 | // be released, or exactly on the vsync period it should be released. | ||
| 90 | // | ||
| 91 | // TODO: handle cases where swap intervals are relatively prime. So far, | ||
| 92 | // only swap intervals of 0, 1 and 2 have been observed, but if 3 were | ||
| 93 | // to be introduced, this would cause an issue. | ||
| 94 | if (swap_interval) { | ||
| 95 | swap_interval = std::min(*swap_interval, item.swap_interval); | ||
| 96 | } else { | ||
| 97 | swap_interval = item.swap_interval; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | // If any new buffers were acquired, we can present. | ||
| 102 | if (has_acquired_buffer) { | ||
| 103 | // Sort by Z-index. | ||
| 104 | std::stable_sort(composition_stack.begin(), composition_stack.end(), | ||
| 105 | [&](auto& l, auto& r) { return l.z_index < r.z_index; }); | ||
| 106 | |||
| 107 | // Composite. | ||
| 108 | nvdisp.Composite(composition_stack); | ||
| 109 | } | ||
| 110 | |||
| 111 | // Render MicroProfile. | ||
| 112 | MicroProfileFlip(); | ||
| 113 | |||
| 114 | // If we advanced, then advance by at least 1 frame. | ||
| 115 | if (swap_interval) { | ||
| 116 | return std::max(*swap_interval, 1U); | ||
| 117 | } | ||
| 118 | |||
| 119 | // Otherwise, advance by exactly one frame. | ||
| 120 | return 1U; | ||
| 121 | } | ||
| 122 | |||
| 123 | void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { | ||
| 124 | // Check if we are tracking a slot with this layer_id. | ||
| 125 | const auto it = m_framebuffers.find(layer_id); | ||
| 126 | if (it == m_framebuffers.end()) { | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | |||
| 130 | // Try to release the buffer item. | ||
| 131 | auto* const layer = display.FindLayer(layer_id); | ||
| 132 | if (layer && it->second.is_acquired) { | ||
| 133 | layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); | ||
| 134 | } | ||
| 135 | |||
| 136 | // Erase the slot. | ||
| 137 | m_framebuffers.erase(it); | ||
| 138 | } | ||
| 139 | |||
| 140 | bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { | ||
| 141 | // Attempt the update. | ||
| 142 | const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); | ||
| 143 | if (status != android::Status::NoError) { | ||
| 144 | return false; | ||
| 145 | } | ||
| 146 | |||
| 147 | // We succeeded, so set the new release frame info. | ||
| 148 | framebuffer.release_frame_number = | ||
| 149 | m_frame_number + std::max(1U, framebuffer.item.swap_interval); | ||
| 150 | framebuffer.is_acquired = true; | ||
| 151 | |||
| 152 | return true; | ||
| 153 | } | ||
| 154 | |||
| 155 | HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, | ||
| 156 | LayerId layer_id) { | ||
| 157 | // Check if this framebuffer is already present. | ||
| 158 | const auto it = m_framebuffers.find(layer_id); | ||
| 159 | if (it != m_framebuffers.end()) { | ||
| 160 | // If it's currently still acquired, we are done. | ||
| 161 | if (it->second.is_acquired) { | ||
| 162 | return CacheStatus::CachedBufferReused; | ||
| 163 | } | ||
| 164 | |||
| 165 | // Try to acquire a new item. | ||
| 166 | if (this->TryAcquireFramebufferLocked(layer, it->second)) { | ||
| 167 | // We got a new item. | ||
| 168 | return CacheStatus::BufferAcquired; | ||
| 169 | } else { | ||
| 170 | // We didn't acquire a new item, but we can reuse the slot. | ||
| 171 | return CacheStatus::CachedBufferReused; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | // Framebuffer is not present, so try to create it. | ||
| 176 | Framebuffer framebuffer{}; | ||
| 177 | |||
| 178 | if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { | ||
| 179 | // Move the buffer item into a new slot. | ||
| 180 | m_framebuffers.emplace(layer_id, std::move(framebuffer)); | ||
| 181 | |||
| 182 | // We succeeded. | ||
| 183 | return CacheStatus::BufferAcquired; | ||
| 184 | } | ||
| 185 | |||
| 186 | // We couldn't acquire the buffer item, so don't create a slot. | ||
| 187 | return CacheStatus::NoBufferAvailable; | ||
| 188 | } | ||
| 189 | |||
| 190 | } // namespace Service::Nvnflinger | ||
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h new file mode 100644 index 000000000..611afc169 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hardware_composer.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <boost/container/flat_map.hpp> | ||
| 8 | |||
| 9 | #include "core/hle/service/nvnflinger/buffer_item.h" | ||
| 10 | |||
| 11 | namespace Service::Nvidia::Devices { | ||
| 12 | class nvdisp_disp0; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::VI { | ||
| 16 | class Display; | ||
| 17 | class Layer; | ||
| 18 | } // namespace Service::VI | ||
| 19 | |||
| 20 | namespace Service::Nvnflinger { | ||
| 21 | |||
| 22 | using LayerId = u64; | ||
| 23 | |||
| 24 | class HardwareComposer { | ||
| 25 | public: | ||
| 26 | explicit HardwareComposer(); | ||
| 27 | ~HardwareComposer(); | ||
| 28 | |||
| 29 | u32 ComposeLocked(VI::Display& display, Nvidia::Devices::nvdisp_disp0& nvdisp, | ||
| 30 | u32 frame_advance); | ||
| 31 | void RemoveLayerLocked(VI::Display& display, LayerId layer_id); | ||
| 32 | |||
| 33 | private: | ||
| 34 | // TODO: do we want to track frame number in vi instead? | ||
| 35 | u64 m_frame_number{0}; | ||
| 36 | |||
| 37 | private: | ||
| 38 | using ReleaseFrameNumber = u64; | ||
| 39 | |||
| 40 | struct Framebuffer { | ||
| 41 | android::BufferItem item{}; | ||
| 42 | ReleaseFrameNumber release_frame_number{}; | ||
| 43 | bool is_acquired{false}; | ||
| 44 | }; | ||
| 45 | |||
| 46 | enum class CacheStatus : u32 { | ||
| 47 | NoBufferAvailable, | ||
| 48 | BufferAcquired, | ||
| 49 | CachedBufferReused, | ||
| 50 | }; | ||
| 51 | |||
| 52 | boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{}; | ||
| 53 | |||
| 54 | private: | ||
| 55 | bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer); | ||
| 56 | CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id); | ||
| 57 | }; | ||
| 58 | |||
| 59 | } // namespace Service::Nvnflinger | ||
diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h new file mode 100644 index 000000000..3af668a25 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hwc_layer.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 8 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | ||
| 9 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 10 | #include "core/hle/service/nvnflinger/ui/fence.h" | ||
| 11 | |||
| 12 | namespace Service::Nvnflinger { | ||
| 13 | |||
| 14 | struct HwcLayer { | ||
| 15 | u32 buffer_handle; | ||
| 16 | u32 offset; | ||
| 17 | android::PixelFormat format; | ||
| 18 | u32 width; | ||
| 19 | u32 height; | ||
| 20 | u32 stride; | ||
| 21 | s32 z_index; | ||
| 22 | android::BufferTransformFlags transform; | ||
| 23 | Common::Rectangle<int> crop_rect; | ||
| 24 | android::Fence acquire_fence; | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace Service::Nvnflinger | ||
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 51133853c..e775a2ca8 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | 18 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" |
| 19 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 19 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| 20 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | 20 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" |
| 21 | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||
| 21 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | 22 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" |
| 22 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 23 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| 23 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | 24 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" |
| @@ -279,45 +280,18 @@ void Nvnflinger::Compose() { | |||
| 279 | SCOPE_EXIT({ display.SignalVSyncEvent(); }); | 280 | SCOPE_EXIT({ display.SignalVSyncEvent(); }); |
| 280 | 281 | ||
| 281 | // Don't do anything for displays without layers. | 282 | // Don't do anything for displays without layers. |
| 282 | if (!display.HasLayers()) | 283 | if (!display.HasLayers()) { |
| 283 | continue; | ||
| 284 | |||
| 285 | // TODO(Subv): Support more than 1 layer. | ||
| 286 | VI::Layer& layer = display.GetLayer(0); | ||
| 287 | |||
| 288 | android::BufferItem buffer{}; | ||
| 289 | const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); | ||
| 290 | |||
| 291 | if (status != android::Status::NoError) { | ||
| 292 | continue; | 284 | continue; |
| 293 | } | 285 | } |
| 294 | 286 | ||
| 295 | const auto& igbp_buffer = *buffer.graphic_buffer; | ||
| 296 | |||
| 297 | if (!system.IsPoweredOn()) { | 287 | if (!system.IsPoweredOn()) { |
| 298 | return; // We are likely shutting down | 288 | return; // We are likely shutting down |
| 299 | } | 289 | } |
| 300 | 290 | ||
| 301 | // Now send the buffer to the GPU for drawing. | ||
| 302 | // TODO(Subv): Support more than just disp0. The display device selection is probably based | ||
| 303 | // on which display we're drawing (Default, Internal, External, etc) | ||
| 304 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); | 291 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); |
| 305 | ASSERT(nvdisp); | 292 | ASSERT(nvdisp); |
| 306 | 293 | ||
| 307 | Common::Rectangle<int> crop_rect{ | 294 | swap_interval = display.GetComposer().ComposeLocked(display, *nvdisp, swap_interval); |
| 308 | static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), | ||
| 309 | static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; | ||
| 310 | |||
| 311 | nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), | ||
| 312 | igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), | ||
| 313 | static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, | ||
| 314 | buffer.fence.fences, buffer.fence.num_fences); | ||
| 315 | |||
| 316 | MicroProfileFlip(); | ||
| 317 | |||
| 318 | swap_interval = buffer.swap_interval; | ||
| 319 | |||
| 320 | layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); | ||
| 321 | } | 295 | } |
| 322 | } | 296 | } |
| 323 | 297 | ||
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 369439142..73ff36620 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h | |||
| @@ -46,6 +46,7 @@ class BufferQueueProducer; | |||
| 46 | namespace Service::Nvnflinger { | 46 | namespace Service::Nvnflinger { |
| 47 | 47 | ||
| 48 | class FbShareBufferManager; | 48 | class FbShareBufferManager; |
| 49 | class HardwareComposer; | ||
| 49 | class HosBinderDriverServer; | 50 | class HosBinderDriverServer; |
| 50 | 51 | ||
| 51 | class Nvnflinger final { | 52 | class Nvnflinger final { |
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index dab1905cc..7f2af9acc 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" | 16 | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" |
| 17 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 17 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| 18 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | 18 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" |
| 19 | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||
| 19 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | 20 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" |
| 20 | #include "core/hle/service/vi/display/vi_display.h" | 21 | #include "core/hle/service/vi/display/vi_display.h" |
| 21 | #include "core/hle/service/vi/layer/vi_layer.h" | 22 | #include "core/hle/service/vi/layer/vi_layer.h" |
| @@ -43,6 +44,7 @@ Display::Display(u64 id, std::string name_, | |||
| 43 | KernelHelpers::ServiceContext& service_context_, Core::System& system_) | 44 | KernelHelpers::ServiceContext& service_context_, Core::System& system_) |
| 44 | : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, | 45 | : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, |
| 45 | service_context{service_context_} { | 46 | service_context{service_context_} { |
| 47 | hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>(); | ||
| 46 | vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); | 48 | vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); |
| 47 | } | 49 | } |
| 48 | 50 | ||
| @@ -81,8 +83,6 @@ void Display::SignalVSyncEvent() { | |||
| 81 | 83 | ||
| 82 | void Display::CreateLayer(u64 layer_id, u32 binder_id, | 84 | void Display::CreateLayer(u64 layer_id, u32 binder_id, |
| 83 | Service::Nvidia::NvCore::Container& nv_core) { | 85 | Service::Nvidia::NvCore::Container& nv_core) { |
| 84 | ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); | ||
| 85 | |||
| 86 | auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); | 86 | auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); |
| 87 | 87 | ||
| 88 | auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); | 88 | auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 8eb8a5155..220292cff 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -11,9 +11,14 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 13 | 13 | ||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Kernel { | 18 | namespace Kernel { |
| 15 | class KEvent; | 19 | class KEvent; |
| 16 | } | 20 | class KReadableEvent; |
| 21 | } // namespace Kernel | ||
| 17 | 22 | ||
| 18 | namespace Service::android { | 23 | namespace Service::android { |
| 19 | class BufferQueueProducer; | 24 | class BufferQueueProducer; |
| @@ -24,8 +29,9 @@ class ServiceContext; | |||
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | namespace Service::Nvnflinger { | 31 | namespace Service::Nvnflinger { |
| 32 | class HardwareComposer; | ||
| 27 | class HosBinderDriverServer; | 33 | class HosBinderDriverServer; |
| 28 | } | 34 | } // namespace Service::Nvnflinger |
| 29 | 35 | ||
| 30 | namespace Service::Nvidia::NvCore { | 36 | namespace Service::Nvidia::NvCore { |
| 31 | class Container; | 37 | class Container; |
| @@ -118,6 +124,10 @@ public: | |||
| 118 | /// | 124 | /// |
| 119 | const Layer* FindLayer(u64 layer_id) const; | 125 | const Layer* FindLayer(u64 layer_id) const; |
| 120 | 126 | ||
| 127 | Nvnflinger::HardwareComposer& GetComposer() const { | ||
| 128 | return *hardware_composer; | ||
| 129 | } | ||
| 130 | |||
| 121 | private: | 131 | private: |
| 122 | u64 display_id; | 132 | u64 display_id; |
| 123 | std::string name; | 133 | std::string name; |
| @@ -125,6 +135,7 @@ private: | |||
| 125 | KernelHelpers::ServiceContext& service_context; | 135 | KernelHelpers::ServiceContext& service_context; |
| 126 | 136 | ||
| 127 | std::vector<std::unique_ptr<Layer>> layers; | 137 | std::vector<std::unique_ptr<Layer>> layers; |
| 138 | std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer; | ||
| 128 | Kernel::KEvent* vsync_event{}; | 139 | Kernel::KEvent* vsync_event{}; |
| 129 | bool is_abandoned{}; | 140 | bool is_abandoned{}; |
| 130 | }; | 141 | }; |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 73058db9a..d508ed28c 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -195,8 +195,9 @@ private: | |||
| 195 | void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { | 195 | void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { |
| 196 | IPC::RequestParser rp{ctx}; | 196 | IPC::RequestParser rp{ctx}; |
| 197 | const u64 buffer_id = rp.PopRaw<u64>(); | 197 | const u64 buffer_id = rp.PopRaw<u64>(); |
| 198 | const u64 aruid = ctx.GetPID(); | ||
| 198 | 199 | ||
| 199 | LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id); | 200 | LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid); |
| 200 | 201 | ||
| 201 | struct OutputParameters { | 202 | struct OutputParameters { |
| 202 | s32 nvmap_handle; | 203 | s32 nvmap_handle; |
| @@ -206,7 +207,7 @@ private: | |||
| 206 | OutputParameters out{}; | 207 | OutputParameters out{}; |
| 207 | Nvnflinger::SharedMemoryPoolLayout layout{}; | 208 | Nvnflinger::SharedMemoryPoolLayout layout{}; |
| 208 | const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( | 209 | const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( |
| 209 | &out.size, &out.nvmap_handle, &layout, buffer_id, 0); | 210 | &out.size, &out.nvmap_handle, &layout, buffer_id, aruid); |
| 210 | 211 | ||
| 211 | ctx.WriteBuffer(&layout, sizeof(layout)); | 212 | ctx.WriteBuffer(&layout, sizeof(layout)); |
| 212 | 213 | ||
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index 10ddc75a7..6a18b76fb 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/math_util.h" | 7 | #include "common/math_util.h" |
| 8 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | 8 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" |
| 9 | #include "core/hle/service/nvnflinger/pixel_format.h" | 9 | #include "core/hle/service/nvnflinger/pixel_format.h" |
| 10 | #include "core/hle/service/nvnflinger/ui/fence.h" | ||
| 10 | 11 | ||
| 11 | namespace Tegra { | 12 | namespace Tegra { |
| 12 | 13 | ||
| @@ -21,7 +22,7 @@ struct FramebufferConfig { | |||
| 21 | u32 stride{}; | 22 | u32 stride{}; |
| 22 | Service::android::PixelFormat pixel_format{}; | 23 | Service::android::PixelFormat pixel_format{}; |
| 23 | Service::android::BufferTransformFlags transform_flags{}; | 24 | Service::android::BufferTransformFlags transform_flags{}; |
| 24 | Common::Rectangle<int> crop_rect; | 25 | Common::Rectangle<int> crop_rect{}; |
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 27 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | 28 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 609704b33..f4a5d831c 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -274,11 +274,6 @@ struct GPU::Impl { | |||
| 274 | } | 274 | } |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | /// Swap buffers (render frame) | ||
| 278 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 279 | gpu_thread.SwapBuffers(framebuffer); | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 277 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 283 | void FlushRegion(DAddr addr, u64 size) { | 278 | void FlushRegion(DAddr addr, u64 size) { |
| 284 | gpu_thread.FlushRegion(addr, size); | 279 | gpu_thread.FlushRegion(addr, size); |
| @@ -313,8 +308,9 @@ struct GPU::Impl { | |||
| 313 | gpu_thread.FlushAndInvalidateRegion(addr, size); | 308 | gpu_thread.FlushAndInvalidateRegion(addr, size); |
| 314 | } | 309 | } |
| 315 | 310 | ||
| 316 | void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | 311 | void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 317 | std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { | 312 | std::vector<Service::Nvidia::NvFence>&& fences) { |
| 313 | size_t num_fences{fences.size()}; | ||
| 318 | size_t current_request_counter{}; | 314 | size_t current_request_counter{}; |
| 319 | { | 315 | { |
| 320 | std::unique_lock<std::mutex> lk(request_swap_mutex); | 316 | std::unique_lock<std::mutex> lk(request_swap_mutex); |
| @@ -328,13 +324,12 @@ struct GPU::Impl { | |||
| 328 | } | 324 | } |
| 329 | } | 325 | } |
| 330 | const auto wait_fence = | 326 | const auto wait_fence = |
| 331 | RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { | 327 | RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { |
| 332 | auto& syncpoint_manager = host1x.GetSyncpointManager(); | 328 | auto& syncpoint_manager = host1x.GetSyncpointManager(); |
| 333 | if (num_fences == 0) { | 329 | if (num_fences == 0) { |
| 334 | renderer->SwapBuffers(framebuffer); | 330 | renderer->Composite(layers); |
| 335 | } | 331 | } |
| 336 | const auto executer = [this, current_request_counter, | 332 | const auto executer = [this, current_request_counter, layers_copy = layers]() { |
| 337 | framebuffer_copy = *framebuffer]() { | ||
| 338 | { | 333 | { |
| 339 | std::unique_lock<std::mutex> lk(request_swap_mutex); | 334 | std::unique_lock<std::mutex> lk(request_swap_mutex); |
| 340 | if (--request_swap_counters[current_request_counter] != 0) { | 335 | if (--request_swap_counters[current_request_counter] != 0) { |
| @@ -342,7 +337,7 @@ struct GPU::Impl { | |||
| 342 | } | 337 | } |
| 343 | free_swap_counters.push_back(current_request_counter); | 338 | free_swap_counters.push_back(current_request_counter); |
| 344 | } | 339 | } |
| 345 | renderer->SwapBuffers(&framebuffer_copy); | 340 | renderer->Composite(layers_copy); |
| 346 | }; | 341 | }; |
| 347 | for (size_t i = 0; i < num_fences; i++) { | 342 | for (size_t i = 0; i < num_fences; i++) { |
| 348 | syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); | 343 | syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); |
| @@ -505,9 +500,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { | |||
| 505 | return impl->ShaderNotify(); | 500 | return impl->ShaderNotify(); |
| 506 | } | 501 | } |
| 507 | 502 | ||
| 508 | void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | 503 | void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 509 | std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { | 504 | std::vector<Service::Nvidia::NvFence>&& fences) { |
| 510 | impl->RequestSwapBuffers(framebuffer, fences, num_fences); | 505 | impl->RequestComposite(std::move(layers), std::move(fences)); |
| 511 | } | 506 | } |
| 512 | 507 | ||
| 513 | u64 GPU::GetTicks() const { | 508 | u64 GPU::GetTicks() const { |
| @@ -554,10 +549,6 @@ void GPU::ClearCdmaInstance(u32 id) { | |||
| 554 | impl->ClearCdmaInstance(id); | 549 | impl->ClearCdmaInstance(id); |
| 555 | } | 550 | } |
| 556 | 551 | ||
| 557 | void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 558 | impl->SwapBuffers(framebuffer); | ||
| 559 | } | ||
| 560 | |||
| 561 | VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { | 552 | VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { |
| 562 | return impl->OnCPURead(addr, size); | 553 | return impl->OnCPURead(addr, size); |
| 563 | } | 554 | } |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index b3c1d15bd..c4602ca37 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -212,8 +212,8 @@ public: | |||
| 212 | 212 | ||
| 213 | void RendererFrameEndNotify(); | 213 | void RendererFrameEndNotify(); |
| 214 | 214 | ||
| 215 | void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | 215 | void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 216 | std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences); | 216 | std::vector<Service::Nvidia::NvFence>&& fences); |
| 217 | 217 | ||
| 218 | /// Performs any additional setup necessary in order to begin GPU emulation. | 218 | /// Performs any additional setup necessary in order to begin GPU emulation. |
| 219 | /// This can be used to launch any necessary threads and register any necessary | 219 | /// This can be used to launch any necessary threads and register any necessary |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 788d4f61e..58d8110b8 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system, | |||
| 40 | } | 40 | } |
| 41 | if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { | 41 | if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { |
| 42 | scheduler.Push(submit_list->channel, std::move(submit_list->entries)); | 42 | scheduler.Push(submit_list->channel, std::move(submit_list->entries)); |
| 43 | } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { | ||
| 44 | renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); | ||
| 45 | } else if (std::holds_alternative<GPUTickCommand>(next.data)) { | 43 | } else if (std::holds_alternative<GPUTickCommand>(next.data)) { |
| 46 | system.GPU().TickWork(); | 44 | system.GPU().TickWork(); |
| 47 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { | 45 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { |
| @@ -78,10 +76,6 @@ void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { | |||
| 78 | PushCommand(SubmitListCommand(channel, std::move(entries))); | 76 | PushCommand(SubmitListCommand(channel, std::move(entries))); |
| 79 | } | 77 | } |
| 80 | 78 | ||
| 81 | void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 82 | PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); | ||
| 83 | } | ||
| 84 | |||
| 85 | void ThreadManager::FlushRegion(DAddr addr, u64 size) { | 79 | void ThreadManager::FlushRegion(DAddr addr, u64 size) { |
| 86 | if (!is_async) { | 80 | if (!is_async) { |
| 87 | // Always flush with synchronous GPU mode | 81 | // Always flush with synchronous GPU mode |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 2de25e9ef..dc0fce9f8 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -44,14 +44,6 @@ struct SubmitListCommand final { | |||
| 44 | Tegra::CommandList entries; | 44 | Tegra::CommandList entries; |
| 45 | }; | 45 | }; |
| 46 | 46 | ||
| 47 | /// Command to signal to the GPU thread that a swap buffers is pending | ||
| 48 | struct SwapBuffersCommand final { | ||
| 49 | explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_) | ||
| 50 | : framebuffer{std::move(framebuffer_)} {} | ||
| 51 | |||
| 52 | std::optional<Tegra::FramebufferConfig> framebuffer; | ||
| 53 | }; | ||
| 54 | |||
| 55 | /// Command to signal to the GPU thread to flush a region | 47 | /// Command to signal to the GPU thread to flush a region |
| 56 | struct FlushRegionCommand final { | 48 | struct FlushRegionCommand final { |
| 57 | explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} | 49 | explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} |
| @@ -81,8 +73,8 @@ struct FlushAndInvalidateRegionCommand final { | |||
| 81 | struct GPUTickCommand final {}; | 73 | struct GPUTickCommand final {}; |
| 82 | 74 | ||
| 83 | using CommandData = | 75 | using CommandData = |
| 84 | std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, | 76 | std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand, |
| 85 | InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>; | 77 | FlushAndInvalidateRegionCommand, GPUTickCommand>; |
| 86 | 78 | ||
| 87 | struct CommandDataContainer { | 79 | struct CommandDataContainer { |
| 88 | CommandDataContainer() = default; | 80 | CommandDataContainer() = default; |
| @@ -118,9 +110,6 @@ public: | |||
| 118 | /// Push GPU command entries to be processed | 110 | /// Push GPU command entries to be processed |
| 119 | void SubmitList(s32 channel, Tegra::CommandList&& entries); | 111 | void SubmitList(s32 channel, Tegra::CommandList&& entries); |
| 120 | 112 | ||
| 121 | /// Swap buffers (render frame) | ||
| 122 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); | ||
| 123 | |||
| 124 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 113 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 125 | void FlushRegion(DAddr addr, u64 size); | 114 | void FlushRegion(DAddr addr, u64 size); |
| 126 | 115 | ||
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 78ea5208b..3ad180f67 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -38,7 +38,7 @@ public: | |||
| 38 | virtual ~RendererBase(); | 38 | virtual ~RendererBase(); |
| 39 | 39 | ||
| 40 | /// Finalize rendering the guest frame and draw into the presentation texture | 40 | /// Finalize rendering the guest frame and draw into the presentation texture |
| 41 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | 41 | virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; |
| 42 | 42 | ||
| 43 | [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; | 43 | [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; |
| 44 | 44 | ||
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp index 078feb925..c89daff53 100644 --- a/src/video_core/renderer_null/renderer_null.cpp +++ b/src/video_core/renderer_null/renderer_null.cpp | |||
| @@ -13,8 +13,8 @@ RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gp | |||
| 13 | 13 | ||
| 14 | RendererNull::~RendererNull() = default; | 14 | RendererNull::~RendererNull() = default; |
| 15 | 15 | ||
| 16 | void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 16 | void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 17 | if (!framebuffer) { | 17 | if (framebuffers.empty()) { |
| 18 | return; | 18 | return; |
| 19 | } | 19 | } |
| 20 | 20 | ||
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h index 9531b43f6..063b476bb 100644 --- a/src/video_core/renderer_null/renderer_null.h +++ b/src/video_core/renderer_null/renderer_null.h | |||
| @@ -17,7 +17,7 @@ public: | |||
| 17 | std::unique_ptr<Core::Frontend::GraphicsContext> context); | 17 | std::unique_ptr<Core::Frontend::GraphicsContext> context); |
| 18 | ~RendererNull() override; | 18 | ~RendererNull() override; |
| 19 | 19 | ||
| 20 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 20 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; |
| 21 | 21 | ||
| 22 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 22 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 23 | return &m_rasterizer; | 23 | return &m_rasterizer; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 10a9f973c..e33a32592 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -125,15 +125,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 125 | 125 | ||
| 126 | RendererOpenGL::~RendererOpenGL() = default; | 126 | RendererOpenGL::~RendererOpenGL() = default; |
| 127 | 127 | ||
| 128 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 128 | void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 129 | if (!framebuffer) { | 129 | if (framebuffers.empty()) { |
| 130 | return; | 130 | return; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | RenderScreenshot(framebuffer); | 133 | RenderScreenshot(framebuffers); |
| 134 | 134 | ||
| 135 | state_tracker.BindFramebuffer(0); | 135 | state_tracker.BindFramebuffer(0); |
| 136 | blit_screen->DrawScreen(std::span(framebuffer, 1), emu_window.GetFramebufferLayout()); | 136 | blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); |
| 137 | 137 | ||
| 138 | ++m_current_frame; | 138 | ++m_current_frame; |
| 139 | 139 | ||
| @@ -159,7 +159,7 @@ void RendererOpenGL::AddTelemetryFields() { | |||
| 159 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); | 159 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig* framebuffer) { | 162 | void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 163 | if (!renderer_settings.screenshot_requested) { | 163 | if (!renderer_settings.screenshot_requested) { |
| 164 | return; | 164 | return; |
| 165 | } | 165 | } |
| @@ -181,7 +181,7 @@ void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig* framebuffe | |||
| 181 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); | 181 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); |
| 182 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); | 182 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); |
| 183 | 183 | ||
| 184 | blit_screen->DrawScreen(std::span(framebuffer, 1), layout); | 184 | blit_screen->DrawScreen(framebuffers, layout); |
| 185 | 185 | ||
| 186 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | 186 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| 187 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); | 187 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index df76d3d05..c4625c96e 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -40,7 +40,7 @@ public: | |||
| 40 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); | 40 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); |
| 41 | ~RendererOpenGL() override; | 41 | ~RendererOpenGL() override; |
| 42 | 42 | ||
| 43 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 43 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; |
| 44 | 44 | ||
| 45 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 45 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 46 | return &rasterizer; | 46 | return &rasterizer; |
| @@ -52,7 +52,7 @@ public: | |||
| 52 | 52 | ||
| 53 | private: | 53 | private: |
| 54 | void AddTelemetryFields(); | 54 | void AddTelemetryFields(); |
| 55 | void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); | 55 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); |
| 56 | 56 | ||
| 57 | Core::TelemetrySession& telemetry_session; | 57 | Core::TelemetrySession& telemetry_session; |
| 58 | Core::Frontend::EmuWindow& emu_window; | 58 | Core::Frontend::EmuWindow& emu_window; |
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 7bff1c436..6ee16595d 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp | |||
| @@ -7,6 +7,20 @@ | |||
| 7 | 7 | ||
| 8 | namespace Vulkan { | 8 | namespace Vulkan { |
| 9 | 9 | ||
| 10 | vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) { | ||
| 11 | const VkBufferCreateInfo dst_buffer_info{ | ||
| 12 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||
| 13 | .pNext = nullptr, | ||
| 14 | .flags = 0, | ||
| 15 | .size = size, | ||
| 16 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, | ||
| 17 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 18 | .queueFamilyIndexCount = 0, | ||
| 19 | .pQueueFamilyIndices = nullptr, | ||
| 20 | }; | ||
| 21 | return allocator.CreateBuffer(dst_buffer_info, usage); | ||
| 22 | } | ||
| 23 | |||
| 10 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { | 24 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { |
| 11 | const VkImageCreateInfo image_ci{ | 25 | const VkImageCreateInfo image_ci{ |
| 12 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | 26 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| @@ -96,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc | |||
| 96 | scheduler.Finish(); | 110 | scheduler.Finish(); |
| 97 | } | 111 | } |
| 98 | 112 | ||
| 113 | void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, | ||
| 114 | VkExtent3D extent) { | ||
| 115 | const VkImageMemoryBarrier read_barrier{ | ||
| 116 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 117 | .pNext = nullptr, | ||
| 118 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 119 | .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, | ||
| 120 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 121 | .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 122 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 123 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 124 | .image = image, | ||
| 125 | .subresourceRange{ | ||
| 126 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 127 | .baseMipLevel = 0, | ||
| 128 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 129 | .baseArrayLayer = 0, | ||
| 130 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 131 | }, | ||
| 132 | }; | ||
| 133 | const VkImageMemoryBarrier image_write_barrier{ | ||
| 134 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 135 | .pNext = nullptr, | ||
| 136 | .srcAccessMask = 0, | ||
| 137 | .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 138 | .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 139 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 140 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 141 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 142 | .image = image, | ||
| 143 | .subresourceRange{ | ||
| 144 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 145 | .baseMipLevel = 0, | ||
| 146 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 147 | .baseArrayLayer = 0, | ||
| 148 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 149 | }, | ||
| 150 | }; | ||
| 151 | static constexpr VkMemoryBarrier memory_write_barrier{ | ||
| 152 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 153 | .pNext = nullptr, | ||
| 154 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 155 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 156 | }; | ||
| 157 | const VkBufferImageCopy copy{ | ||
| 158 | .bufferOffset = 0, | ||
| 159 | .bufferRowLength = 0, | ||
| 160 | .bufferImageHeight = 0, | ||
| 161 | .imageSubresource{ | ||
| 162 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 163 | .mipLevel = 0, | ||
| 164 | .baseArrayLayer = 0, | ||
| 165 | .layerCount = 1, | ||
| 166 | }, | ||
| 167 | .imageOffset{.x = 0, .y = 0, .z = 0}, | ||
| 168 | .imageExtent{extent}, | ||
| 169 | }; | ||
| 170 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||
| 171 | read_barrier); | ||
| 172 | cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); | ||
| 173 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, | ||
| 174 | memory_write_barrier, nullptr, image_write_barrier); | ||
| 175 | } | ||
| 176 | |||
| 99 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { | 177 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { |
| 100 | return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | 178 | return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ |
| 101 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 179 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index fb4e4a8e4..1104aaa15 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h | |||
| @@ -11,12 +11,16 @@ namespace Vulkan { | |||
| 11 | 11 | ||
| 12 | #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) | 12 | #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) |
| 13 | 13 | ||
| 14 | vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage); | ||
| 15 | |||
| 14 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); | 16 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); |
| 15 | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, | 17 | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, |
| 16 | VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); | 18 | VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); |
| 17 | void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, | 19 | void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, |
| 18 | vk::Image& image, VkExtent2D dimensions, VkFormat format, | 20 | vk::Image& image, VkExtent2D dimensions, VkFormat format, |
| 19 | std::span<const u8> initial_contents = {}); | 21 | std::span<const u8> initial_contents = {}); |
| 22 | void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, | ||
| 23 | VkExtent3D extent); | ||
| 20 | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); | 24 | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); |
| 21 | 25 | ||
| 22 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); | 26 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 77837adde..48a105327 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -20,12 +20,14 @@ | |||
| 20 | #include "core/frontend/graphics_context.h" | 20 | #include "core/frontend/graphics_context.h" |
| 21 | #include "core/telemetry_session.h" | 21 | #include "core/telemetry_session.h" |
| 22 | #include "video_core/gpu.h" | 22 | #include "video_core/gpu.h" |
| 23 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 23 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 24 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 24 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 25 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| 25 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | 26 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| 26 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 27 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 27 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 28 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 28 | #include "video_core/renderer_vulkan/vk_swapchain.h" | 29 | #include "video_core/renderer_vulkan/vk_swapchain.h" |
| 30 | #include "video_core/textures/decoders.h" | ||
| 29 | #include "video_core/vulkan_common/vulkan_debug_callback.h" | 31 | #include "video_core/vulkan_common/vulkan_debug_callback.h" |
| 30 | #include "video_core/vulkan_common/vulkan_device.h" | 32 | #include "video_core/vulkan_common/vulkan_device.h" |
| 31 | #include "video_core/vulkan_common/vulkan_instance.h" | 33 | #include "video_core/vulkan_common/vulkan_instance.h" |
| @@ -116,18 +118,20 @@ RendererVulkan::~RendererVulkan() { | |||
| 116 | void(device.GetLogical().WaitIdle()); | 118 | void(device.GetLogical().WaitIdle()); |
| 117 | } | 119 | } |
| 118 | 120 | ||
| 119 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 121 | void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 120 | if (!framebuffer) { | 122 | if (framebuffers.empty()) { |
| 121 | return; | 123 | return; |
| 122 | } | 124 | } |
| 125 | |||
| 123 | SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); | 126 | SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); |
| 127 | |||
| 124 | if (!render_window.IsShown()) { | 128 | if (!render_window.IsShown()) { |
| 125 | return; | 129 | return; |
| 126 | } | 130 | } |
| 127 | 131 | ||
| 128 | RenderScreenshot(framebuffer); | 132 | RenderScreenshot(framebuffers); |
| 129 | Frame* frame = present_manager.GetRenderFrame(); | 133 | Frame* frame = present_manager.GetRenderFrame(); |
| 130 | blit_swapchain.DrawToFrame(rasterizer, frame, std::span(framebuffer, 1), | 134 | blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers, |
| 131 | render_window.GetFramebufferLayout(), swapchain.GetImageCount(), | 135 | render_window.GetFramebufferLayout(), swapchain.GetImageCount(), |
| 132 | swapchain.GetImageViewFormat()); | 136 | swapchain.GetImageViewFormat()); |
| 133 | scheduler.Flush(*frame->render_ready); | 137 | scheduler.Flush(*frame->render_ready); |
| @@ -163,156 +167,37 @@ void RendererVulkan::Report() const { | |||
| 163 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | 167 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |
| 164 | } | 168 | } |
| 165 | 169 | ||
| 166 | void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig* framebuffer) { | 170 | void Vulkan::RendererVulkan::RenderScreenshot( |
| 171 | std::span<const Tegra::FramebufferConfig> framebuffers) { | ||
| 167 | if (!renderer_settings.screenshot_requested) { | 172 | if (!renderer_settings.screenshot_requested) { |
| 168 | return; | 173 | return; |
| 169 | } | 174 | } |
| 175 | |||
| 176 | constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; | ||
| 170 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; | 177 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; |
| 171 | auto frame = [&]() { | ||
| 172 | vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{ | ||
| 173 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||
| 174 | .pNext = nullptr, | ||
| 175 | .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, | ||
| 176 | .imageType = VK_IMAGE_TYPE_2D, | ||
| 177 | .format = VK_FORMAT_B8G8R8A8_UNORM, | ||
| 178 | .extent = | ||
| 179 | { | ||
| 180 | .width = layout.width, | ||
| 181 | .height = layout.height, | ||
| 182 | .depth = 1, | ||
| 183 | }, | ||
| 184 | .mipLevels = 1, | ||
| 185 | .arrayLayers = 1, | ||
| 186 | .samples = VK_SAMPLE_COUNT_1_BIT, | ||
| 187 | .tiling = VK_IMAGE_TILING_OPTIMAL, | ||
| 188 | .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | | ||
| 189 | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | ||
| 190 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 191 | .queueFamilyIndexCount = 0, | ||
| 192 | .pQueueFamilyIndices = nullptr, | ||
| 193 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 194 | }); | ||
| 195 | 178 | ||
| 196 | vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | 179 | auto frame = [&]() { |
| 197 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 180 | Frame f{}; |
| 198 | .pNext = nullptr, | 181 | f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, |
| 199 | .flags = 0, | 182 | ScreenshotFormat); |
| 200 | .image = *staging_image, | 183 | f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); |
| 201 | .viewType = VK_IMAGE_VIEW_TYPE_2D, | 184 | f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); |
| 202 | .format = VK_FORMAT_B8G8R8A8_UNORM, | 185 | return f; |
| 203 | .components{ | ||
| 204 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 205 | .g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 206 | .b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 207 | .a = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 208 | }, | ||
| 209 | .subresourceRange{ | ||
| 210 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 211 | .baseMipLevel = 0, | ||
| 212 | .levelCount = 1, | ||
| 213 | .baseArrayLayer = 0, | ||
| 214 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 215 | }, | ||
| 216 | }); | ||
| 217 | vk::Framebuffer screenshot_fb = | ||
| 218 | blit_screenshot.CreateFramebuffer(layout, *dst_view, VK_FORMAT_B8G8R8A8_UNORM); | ||
| 219 | return Frame{ | ||
| 220 | .width = layout.width, | ||
| 221 | .height = layout.height, | ||
| 222 | .image = std::move(staging_image), | ||
| 223 | .image_view = std::move(dst_view), | ||
| 224 | .framebuffer = std::move(screenshot_fb), | ||
| 225 | .cmdbuf{}, | ||
| 226 | .render_ready{}, | ||
| 227 | .present_done{}, | ||
| 228 | }; | ||
| 229 | }(); | 186 | }(); |
| 230 | 187 | ||
| 231 | blit_screenshot.DrawToFrame(rasterizer, &frame, std::span(framebuffer, 1), layout, 1, | 188 | blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, |
| 232 | VK_FORMAT_B8G8R8A8_UNORM); | 189 | VK_FORMAT_B8G8R8A8_UNORM); |
| 233 | 190 | ||
| 234 | const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); | 191 | const auto dst_buffer = CreateWrappedBuffer( |
| 235 | const VkBufferCreateInfo dst_buffer_info{ | 192 | memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), |
| 236 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 193 | MemoryUsage::Download); |
| 237 | .pNext = nullptr, | ||
| 238 | .flags = 0, | ||
| 239 | .size = buffer_size, | ||
| 240 | .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, | ||
| 241 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 242 | .queueFamilyIndexCount = 0, | ||
| 243 | .pQueueFamilyIndices = nullptr, | ||
| 244 | }; | ||
| 245 | const vk::Buffer dst_buffer = | ||
| 246 | memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download); | ||
| 247 | 194 | ||
| 248 | scheduler.RequestOutsideRenderPassOperationContext(); | 195 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 249 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | 196 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { |
| 250 | const VkImageMemoryBarrier read_barrier{ | 197 | DownloadColorImage(cmdbuf, *frame.image, *dst_buffer, |
| 251 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | 198 | VkExtent3D{layout.width, layout.height, 1}); |
| 252 | .pNext = nullptr, | ||
| 253 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 254 | .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, | ||
| 255 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 256 | .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 257 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 258 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 259 | .image = *frame.image, | ||
| 260 | .subresourceRange{ | ||
| 261 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 262 | .baseMipLevel = 0, | ||
| 263 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 264 | .baseArrayLayer = 0, | ||
| 265 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 266 | }, | ||
| 267 | }; | ||
| 268 | const VkImageMemoryBarrier image_write_barrier{ | ||
| 269 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 270 | .pNext = nullptr, | ||
| 271 | .srcAccessMask = 0, | ||
| 272 | .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 273 | .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 274 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 275 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 276 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 277 | .image = *frame.image, | ||
| 278 | .subresourceRange{ | ||
| 279 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 280 | .baseMipLevel = 0, | ||
| 281 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 282 | .baseArrayLayer = 0, | ||
| 283 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 284 | }, | ||
| 285 | }; | ||
| 286 | static constexpr VkMemoryBarrier memory_write_barrier{ | ||
| 287 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 288 | .pNext = nullptr, | ||
| 289 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 290 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 291 | }; | ||
| 292 | const VkBufferImageCopy copy{ | ||
| 293 | .bufferOffset = 0, | ||
| 294 | .bufferRowLength = 0, | ||
| 295 | .bufferImageHeight = 0, | ||
| 296 | .imageSubresource{ | ||
| 297 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 298 | .mipLevel = 0, | ||
| 299 | .baseArrayLayer = 0, | ||
| 300 | .layerCount = 1, | ||
| 301 | }, | ||
| 302 | .imageOffset{.x = 0, .y = 0, .z = 0}, | ||
| 303 | .imageExtent{ | ||
| 304 | .width = layout.width, | ||
| 305 | .height = layout.height, | ||
| 306 | .depth = 1, | ||
| 307 | }, | ||
| 308 | }; | ||
| 309 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 310 | 0, read_barrier); | ||
| 311 | cmdbuf.CopyImageToBuffer(*frame.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer, | ||
| 312 | copy); | ||
| 313 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 314 | 0, memory_write_barrier, nullptr, image_write_barrier); | ||
| 315 | }); | 199 | }); |
| 200 | |||
| 316 | // Ensure the copy is fully completed before saving the screenshot | 201 | // Ensure the copy is fully completed before saving the screenshot |
| 317 | scheduler.Finish(); | 202 | scheduler.Finish(); |
| 318 | 203 | ||
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index bdeb43a54..c6d8a0f21 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -46,7 +46,7 @@ public: | |||
| 46 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); | 46 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); |
| 47 | ~RendererVulkan() override; | 47 | ~RendererVulkan() override; |
| 48 | 48 | ||
| 49 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 49 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; |
| 50 | 50 | ||
| 51 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 51 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 52 | return &rasterizer; | 52 | return &rasterizer; |
| @@ -59,7 +59,7 @@ public: | |||
| 59 | private: | 59 | private: |
| 60 | void Report() const; | 60 | void Report() const; |
| 61 | 61 | ||
| 62 | void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); | 62 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); |
| 63 | 63 | ||
| 64 | Core::TelemetrySession& telemetry_session; | 64 | Core::TelemetrySession& telemetry_session; |
| 65 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 65 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index b2dcbf80b..2275fcc46 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -115,7 +115,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | |||
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, | 117 | vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, |
| 118 | const VkImageView& image_view, | 118 | VkImageView image_view, |
| 119 | VkFormat current_view_format) { | 119 | VkFormat current_view_format) { |
| 120 | const bool format_updated = | 120 | const bool format_updated = |
| 121 | std::exchange(swapchain_view_format, current_view_format) != current_view_format; | 121 | std::exchange(swapchain_view_format, current_view_format) != current_view_format; |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 9a3476c77..cbdf2d5d0 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h | |||
| @@ -56,7 +56,7 @@ public: | |||
| 56 | VkFormat current_swapchain_view_format); | 56 | VkFormat current_swapchain_view_format); |
| 57 | 57 | ||
| 58 | [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, | 58 | [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, |
| 59 | const VkImageView& image_view, | 59 | VkImageView image_view, |
| 60 | VkFormat current_view_format); | 60 | VkFormat current_view_format); |
| 61 | 61 | ||
| 62 | private: | 62 | private: |