mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-10-31 14:56:40 +08:00 
			
		
		
		
	nvnflinger/gpu: implement layer stack composition
This commit is contained in:
		
							parent
							
								
									10cf058518
								
							
						
					
					
						commit
						a595e9e8a7
					
				| @ -775,6 +775,9 @@ add_library(core STATIC | |||||||
|     hle/service/nvnflinger/graphic_buffer_producer.h |     hle/service/nvnflinger/graphic_buffer_producer.h | ||||||
|     hle/service/nvnflinger/hos_binder_driver_server.cpp |     hle/service/nvnflinger/hos_binder_driver_server.cpp | ||||||
|     hle/service/nvnflinger/hos_binder_driver_server.h |     hle/service/nvnflinger/hos_binder_driver_server.h | ||||||
|  |     hle/service/nvnflinger/hardware_composer.cpp | ||||||
|  |     hle/service/nvnflinger/hardware_composer.h | ||||||
|  |     hle/service/nvnflinger/hwc_layer.h | ||||||
|     hle/service/nvnflinger/nvnflinger.cpp |     hle/service/nvnflinger/nvnflinger.cpp | ||||||
|     hle/service/nvnflinger/nvnflinger.h |     hle/service/nvnflinger/nvnflinger.h | ||||||
|     hle/service/nvnflinger/parcel.h |     hle/service/nvnflinger/parcel.h | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 | ||||||
| // SPDX-License-Identifier: GPL-2.0-or-later
 | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
| 
 | 
 | ||||||
|  | #include <boost/container/small_vector.hpp> | ||||||
|  | 
 | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| @ -38,19 +40,30 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in | |||||||
| void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} | void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} | ||||||
| void nvdisp_disp0::OnClose(DeviceFD fd) {} | void nvdisp_disp0::OnClose(DeviceFD fd) {} | ||||||
| 
 | 
 | ||||||
| void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, | void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers) { | ||||||
|                         u32 height, u32 stride, android::BufferTransformFlags transform, |     std::vector<Tegra::FramebufferConfig> output_layers; | ||||||
|                         const Common::Rectangle<int>& crop_rect, |     std::vector<Service::Nvidia::NvFence> output_fences; | ||||||
|                         std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { |     output_layers.reserve(sorted_layers.size()); | ||||||
|     const DAddr addr = nvmap.GetHandleAddress(buffer_handle); |     output_fences.reserve(sorted_layers.size()); | ||||||
|     LOG_TRACE(Service, |  | ||||||
|               "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", |  | ||||||
|               addr, offset, width, height, stride, format); |  | ||||||
| 
 | 
 | ||||||
|     const Tegra::FramebufferConfig framebuffer{addr,   offset, width,     height, |     for (auto& layer : sorted_layers) { | ||||||
|                                                stride, format, transform, crop_rect}; |         output_layers.emplace_back(Tegra::FramebufferConfig{ | ||||||
|  |             .address = nvmap.GetHandleAddress(layer.buffer_handle), | ||||||
|  |             .offset = layer.offset, | ||||||
|  |             .width = layer.width, | ||||||
|  |             .height = layer.height, | ||||||
|  |             .stride = layer.stride, | ||||||
|  |             .pixel_format = layer.format, | ||||||
|  |             .transform_flags = layer.transform, | ||||||
|  |             .crop_rect = layer.crop_rect, | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|     system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); |         for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { | ||||||
|  |             output_fences.push_back(layer.acquire_fence.fences[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences)); | ||||||
|     system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); |     system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); | ||||||
|     system.GetPerfStats().EndSystemFrame(); |     system.GetPerfStats().EndSystemFrame(); | ||||||
|     system.GetPerfStats().BeginSystemFrame(); |     system.GetPerfStats().BeginSystemFrame(); | ||||||
|  | |||||||
| @ -8,8 +8,7 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "core/hle/service/nvdrv/devices/nvdevice.h" | #include "core/hle/service/nvdrv/devices/nvdevice.h" | ||||||
| #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||||||
| #include "core/hle/service/nvnflinger/pixel_format.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Service::Nvidia::NvCore { | namespace Service::Nvidia::NvCore { | ||||||
| class Container; | class Container; | ||||||
| @ -35,11 +34,8 @@ public: | |||||||
|     void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; |     void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; | ||||||
|     void OnClose(DeviceFD fd) override; |     void OnClose(DeviceFD fd) override; | ||||||
| 
 | 
 | ||||||
|     /// Performs a screen flip, drawing the buffer pointed to by the handle.
 |     /// Performs a screen flip, compositing each buffer.
 | ||||||
|     void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, |     void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers); | ||||||
|               u32 stride, android::BufferTransformFlags transform, |  | ||||||
|               const Common::Rectangle<int>& crop_rect, |  | ||||||
|               std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); |  | ||||||
| 
 | 
 | ||||||
|     Kernel::KEvent* QueryEvent(u32 event_id) override; |     Kernel::KEvent* QueryEvent(u32 event_id) override; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ public: | |||||||
|     bool is_droppable{}; |     bool is_droppable{}; | ||||||
|     bool acquire_called{}; |     bool acquire_called{}; | ||||||
|     bool transform_to_display_inverse{}; |     bool transform_to_display_inverse{}; | ||||||
|     s32 swap_interval{}; |     u32 swap_interval{}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Service::android
 | } // namespace Service::android
 | ||||||
|  | |||||||
							
								
								
									
										190
									
								
								src/core/hle/service/nvnflinger/hardware_composer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/core/hle/service/nvnflinger/hardware_composer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,190 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #include <boost/container/small_vector.hpp> | ||||||
|  | 
 | ||||||
|  | #include "common/microprofile.h" | ||||||
|  | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" | ||||||
|  | #include "core/hle/service/nvnflinger/buffer_item.h" | ||||||
|  | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | ||||||
|  | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | ||||||
|  | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||||||
|  | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||||||
|  | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | ||||||
|  | #include "core/hle/service/vi/display/vi_display.h" | ||||||
|  | #include "core/hle/service/vi/layer/vi_layer.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::Nvnflinger { | ||||||
|  | 
 | ||||||
|  | HardwareComposer::HardwareComposer() = default; | ||||||
|  | HardwareComposer::~HardwareComposer() = default; | ||||||
|  | 
 | ||||||
|  | u32 HardwareComposer::ComposeLocked(VI::Display& display, Nvidia::Devices::nvdisp_disp0& nvdisp, | ||||||
|  |                                     u32 frame_advance) { | ||||||
|  |     boost::container::small_vector<HwcLayer, 2> composition_stack; | ||||||
|  | 
 | ||||||
|  |     m_frame_number += frame_advance; | ||||||
|  | 
 | ||||||
|  |     // Release any necessary framebuffers.
 | ||||||
|  |     for (auto& [layer_id, framebuffer] : m_framebuffers) { | ||||||
|  |         if (framebuffer.release_frame_number > m_frame_number) { | ||||||
|  |             // Not yet ready to release this framebuffer.
 | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!framebuffer.is_acquired) { | ||||||
|  |             // Already released.
 | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { | ||||||
|  |             // TODO: support release fence
 | ||||||
|  |             // This is needed to prevent screen tearing
 | ||||||
|  |             layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); | ||||||
|  |             framebuffer.is_acquired = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Determine the number of vsync periods to wait before composing again.
 | ||||||
|  |     std::optional<u32> swap_interval{}; | ||||||
|  |     bool has_acquired_buffer{}; | ||||||
|  | 
 | ||||||
|  |     // Acquire all necessary framebuffers.
 | ||||||
|  |     for (size_t i = 0; i < display.GetNumLayers(); i++) { | ||||||
|  |         auto& layer = display.GetLayer(i); | ||||||
|  |         auto layer_id = layer.GetLayerId(); | ||||||
|  | 
 | ||||||
|  |         // Try to fetch the framebuffer (either new or stale).
 | ||||||
|  |         const auto result = this->CacheFramebufferLocked(layer, layer_id); | ||||||
|  | 
 | ||||||
|  |         // If we failed, skip this layer.
 | ||||||
|  |         if (result == CacheStatus::NoBufferAvailable) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // If we acquired a new buffer, we need to present.
 | ||||||
|  |         if (result == CacheStatus::BufferAcquired) { | ||||||
|  |             has_acquired_buffer = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const auto& buffer = m_framebuffers[layer_id]; | ||||||
|  |         const auto& item = buffer.item; | ||||||
|  |         const auto& igbp_buffer = *item.graphic_buffer; | ||||||
|  | 
 | ||||||
|  |         // TODO: get proper Z-index from layer
 | ||||||
|  |         composition_stack.emplace_back(HwcLayer{ | ||||||
|  |             .buffer_handle = igbp_buffer.BufferId(), | ||||||
|  |             .offset = igbp_buffer.Offset(), | ||||||
|  |             .format = igbp_buffer.ExternalFormat(), | ||||||
|  |             .width = igbp_buffer.Width(), | ||||||
|  |             .height = igbp_buffer.Height(), | ||||||
|  |             .stride = igbp_buffer.Stride(), | ||||||
|  |             .z_index = 0, | ||||||
|  |             .transform = static_cast<android::BufferTransformFlags>(item.transform), | ||||||
|  |             .crop_rect = item.crop, | ||||||
|  |             .acquire_fence = item.fence, | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // We need to compose again either before this frame is supposed to
 | ||||||
|  |         // be released, or exactly on the vsync period it should be released.
 | ||||||
|  |         //
 | ||||||
|  |         // TODO: handle cases where swap intervals are relatively prime. So far,
 | ||||||
|  |         // only swap intervals of 0, 1 and 2 have been observed, but if 3 were
 | ||||||
|  |         // to be introduced, this would cause an issue.
 | ||||||
|  |         if (swap_interval) { | ||||||
|  |             swap_interval = std::min(*swap_interval, item.swap_interval); | ||||||
|  |         } else { | ||||||
|  |             swap_interval = item.swap_interval; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // If any new buffers were acquired, we can present.
 | ||||||
|  |     if (has_acquired_buffer) { | ||||||
|  |         // Sort by Z-index.
 | ||||||
|  |         std::stable_sort(composition_stack.begin(), composition_stack.end(), | ||||||
|  |                          [&](auto& l, auto& r) { return l.z_index < r.z_index; }); | ||||||
|  | 
 | ||||||
|  |         // Composite.
 | ||||||
|  |         nvdisp.Composite(composition_stack); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Render MicroProfile.
 | ||||||
|  |     MicroProfileFlip(); | ||||||
|  | 
 | ||||||
|  |     // If we advanced, then advance by at least 1 frame.
 | ||||||
|  |     if (swap_interval) { | ||||||
|  |         return std::max(*swap_interval, 1U); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Otherwise, advance by exactly one frame.
 | ||||||
|  |     return 1U; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { | ||||||
|  |     // Check if we are tracking a slot with this layer_id.
 | ||||||
|  |     const auto it = m_framebuffers.find(layer_id); | ||||||
|  |     if (it == m_framebuffers.end()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Try to release the buffer item.
 | ||||||
|  |     auto* const layer = display.FindLayer(layer_id); | ||||||
|  |     if (layer && it->second.is_acquired) { | ||||||
|  |         layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Erase the slot.
 | ||||||
|  |     m_framebuffers.erase(it); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { | ||||||
|  |     // Attempt the update.
 | ||||||
|  |     const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); | ||||||
|  |     if (status != android::Status::NoError) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // We succeeded, so set the new release frame info.
 | ||||||
|  |     framebuffer.release_frame_number = | ||||||
|  |         m_frame_number + std::max(1U, framebuffer.item.swap_interval); | ||||||
|  |     framebuffer.is_acquired = true; | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, | ||||||
|  |                                                                        LayerId layer_id) { | ||||||
|  |     // Check if this framebuffer is already present.
 | ||||||
|  |     const auto it = m_framebuffers.find(layer_id); | ||||||
|  |     if (it != m_framebuffers.end()) { | ||||||
|  |         // If it's currently still acquired, we are done.
 | ||||||
|  |         if (it->second.is_acquired) { | ||||||
|  |             return CacheStatus::CachedBufferReused; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Try to acquire a new item.
 | ||||||
|  |         if (this->TryAcquireFramebufferLocked(layer, it->second)) { | ||||||
|  |             // We got a new item.
 | ||||||
|  |             return CacheStatus::BufferAcquired; | ||||||
|  |         } else { | ||||||
|  |             // We didn't acquire a new item, but we can reuse the slot.
 | ||||||
|  |             return CacheStatus::CachedBufferReused; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Framebuffer is not present, so try to create it.
 | ||||||
|  |     Framebuffer framebuffer{}; | ||||||
|  | 
 | ||||||
|  |     if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { | ||||||
|  |         // Move the buffer item into a new slot.
 | ||||||
|  |         m_framebuffers.emplace(layer_id, std::move(framebuffer)); | ||||||
|  | 
 | ||||||
|  |         // We succeeded.
 | ||||||
|  |         return CacheStatus::BufferAcquired; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // We couldn't acquire the buffer item, so don't create a slot.
 | ||||||
|  |     return CacheStatus::NoBufferAvailable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Service::Nvnflinger
 | ||||||
							
								
								
									
										59
									
								
								src/core/hle/service/nvnflinger/hardware_composer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/core/hle/service/nvnflinger/hardware_composer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <boost/container/flat_map.hpp> | ||||||
|  | 
 | ||||||
|  | #include "core/hle/service/nvnflinger/buffer_item.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::Nvidia::Devices { | ||||||
|  | class nvdisp_disp0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Service::VI { | ||||||
|  | class Display; | ||||||
|  | class Layer; | ||||||
|  | } // namespace Service::VI
 | ||||||
|  | 
 | ||||||
|  | namespace Service::Nvnflinger { | ||||||
|  | 
 | ||||||
|  | using LayerId = u64; | ||||||
|  | 
 | ||||||
|  | class HardwareComposer { | ||||||
|  | public: | ||||||
|  |     explicit HardwareComposer(); | ||||||
|  |     ~HardwareComposer(); | ||||||
|  | 
 | ||||||
|  |     u32 ComposeLocked(VI::Display& display, Nvidia::Devices::nvdisp_disp0& nvdisp, | ||||||
|  |                       u32 frame_advance); | ||||||
|  |     void RemoveLayerLocked(VI::Display& display, LayerId layer_id); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     // TODO: do we want to track frame number in vi instead?
 | ||||||
|  |     u64 m_frame_number{0}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     using ReleaseFrameNumber = u64; | ||||||
|  | 
 | ||||||
|  |     struct Framebuffer { | ||||||
|  |         android::BufferItem item{}; | ||||||
|  |         ReleaseFrameNumber release_frame_number{}; | ||||||
|  |         bool is_acquired{false}; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     enum class CacheStatus : u32 { | ||||||
|  |         NoBufferAvailable, | ||||||
|  |         BufferAcquired, | ||||||
|  |         CachedBufferReused, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer); | ||||||
|  |     CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Service::Nvnflinger
 | ||||||
							
								
								
									
										27
									
								
								src/core/hle/service/nvnflinger/hwc_layer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/core/hle/service/nvnflinger/hwc_layer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/math_util.h" | ||||||
|  | #include "core/hle/service/nvdrv/nvdata.h" | ||||||
|  | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | ||||||
|  | #include "core/hle/service/nvnflinger/pixel_format.h" | ||||||
|  | #include "core/hle/service/nvnflinger/ui/fence.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::Nvnflinger { | ||||||
|  | 
 | ||||||
|  | struct HwcLayer { | ||||||
|  |     u32 buffer_handle; | ||||||
|  |     u32 offset; | ||||||
|  |     android::PixelFormat format; | ||||||
|  |     u32 width; | ||||||
|  |     u32 height; | ||||||
|  |     u32 stride; | ||||||
|  |     s32 z_index; | ||||||
|  |     android::BufferTransformFlags transform; | ||||||
|  |     Common::Rectangle<int> crop_rect; | ||||||
|  |     android::Fence acquire_fence; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Service::Nvnflinger
 | ||||||
| @ -18,6 +18,7 @@ | |||||||
| #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | ||||||
| #include "core/hle/service/nvnflinger/buffer_queue_core.h" | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | ||||||
| #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | ||||||
|  | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||||||
| #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | ||||||
| #include "core/hle/service/nvnflinger/nvnflinger.h" | #include "core/hle/service/nvnflinger/nvnflinger.h" | ||||||
| #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | ||||||
| @ -279,45 +280,18 @@ void Nvnflinger::Compose() { | |||||||
|         SCOPE_EXIT({ display.SignalVSyncEvent(); }); |         SCOPE_EXIT({ display.SignalVSyncEvent(); }); | ||||||
| 
 | 
 | ||||||
|         // Don't do anything for displays without layers.
 |         // Don't do anything for displays without layers.
 | ||||||
|         if (!display.HasLayers()) |         if (!display.HasLayers()) { | ||||||
|             continue; |  | ||||||
| 
 |  | ||||||
|         // TODO(Subv): Support more than 1 layer.
 |  | ||||||
|         VI::Layer& layer = display.GetLayer(0); |  | ||||||
| 
 |  | ||||||
|         android::BufferItem buffer{}; |  | ||||||
|         const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); |  | ||||||
| 
 |  | ||||||
|         if (status != android::Status::NoError) { |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const auto& igbp_buffer = *buffer.graphic_buffer; |  | ||||||
| 
 |  | ||||||
|         if (!system.IsPoweredOn()) { |         if (!system.IsPoweredOn()) { | ||||||
|             return; // We are likely shutting down
 |             return; // We are likely shutting down
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Now send the buffer to the GPU for drawing.
 |  | ||||||
|         // TODO(Subv): Support more than just disp0. The display device selection is probably based
 |  | ||||||
|         // on which display we're drawing (Default, Internal, External, etc)
 |  | ||||||
|         auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); |         auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); | ||||||
|         ASSERT(nvdisp); |         ASSERT(nvdisp); | ||||||
| 
 | 
 | ||||||
|         Common::Rectangle<int> crop_rect{ |         swap_interval = display.GetComposer().ComposeLocked(display, *nvdisp, swap_interval); | ||||||
|             static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), |  | ||||||
|             static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; |  | ||||||
| 
 |  | ||||||
|         nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), |  | ||||||
|                      igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), |  | ||||||
|                      static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, |  | ||||||
|                      buffer.fence.fences, buffer.fence.num_fences); |  | ||||||
| 
 |  | ||||||
|         MicroProfileFlip(); |  | ||||||
| 
 |  | ||||||
|         swap_interval = buffer.swap_interval; |  | ||||||
| 
 |  | ||||||
|         layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ class BufferQueueProducer; | |||||||
| namespace Service::Nvnflinger { | namespace Service::Nvnflinger { | ||||||
| 
 | 
 | ||||||
| class FbShareBufferManager; | class FbShareBufferManager; | ||||||
|  | class HardwareComposer; | ||||||
| class HosBinderDriverServer; | class HosBinderDriverServer; | ||||||
| 
 | 
 | ||||||
| class Nvnflinger final { | class Nvnflinger final { | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ | |||||||
| #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" | ||||||
| #include "core/hle/service/nvnflinger/buffer_queue_core.h" | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | ||||||
| #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | ||||||
|  | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||||||
| #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | ||||||
| #include "core/hle/service/vi/display/vi_display.h" | #include "core/hle/service/vi/display/vi_display.h" | ||||||
| #include "core/hle/service/vi/layer/vi_layer.h" | #include "core/hle/service/vi/layer/vi_layer.h" | ||||||
| @ -43,6 +44,7 @@ Display::Display(u64 id, std::string name_, | |||||||
|                  KernelHelpers::ServiceContext& service_context_, Core::System& system_) |                  KernelHelpers::ServiceContext& service_context_, Core::System& system_) | ||||||
|     : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, |     : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, | ||||||
|       service_context{service_context_} { |       service_context{service_context_} { | ||||||
|  |     hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>(); | ||||||
|     vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); |     vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -81,8 +83,6 @@ void Display::SignalVSyncEvent() { | |||||||
| 
 | 
 | ||||||
| void Display::CreateLayer(u64 layer_id, u32 binder_id, | void Display::CreateLayer(u64 layer_id, u32 binder_id, | ||||||
|                           Service::Nvidia::NvCore::Container& nv_core) { |                           Service::Nvidia::NvCore::Container& nv_core) { | ||||||
|     ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); |  | ||||||
| 
 |  | ||||||
|     auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); |     auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); | ||||||
| 
 | 
 | ||||||
|     auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); |     auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); | ||||||
|  | |||||||
| @ -11,9 +11,14 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| class KEvent; | class KEvent; | ||||||
| } | class KReadableEvent; | ||||||
|  | } // namespace Kernel
 | ||||||
| 
 | 
 | ||||||
| namespace Service::android { | namespace Service::android { | ||||||
| class BufferQueueProducer; | class BufferQueueProducer; | ||||||
| @ -24,8 +29,9 @@ class ServiceContext; | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace Service::Nvnflinger { | namespace Service::Nvnflinger { | ||||||
|  | class HardwareComposer; | ||||||
| class HosBinderDriverServer; | class HosBinderDriverServer; | ||||||
| } | } // namespace Service::Nvnflinger
 | ||||||
| 
 | 
 | ||||||
| namespace Service::Nvidia::NvCore { | namespace Service::Nvidia::NvCore { | ||||||
| class Container; | class Container; | ||||||
| @ -118,6 +124,10 @@ public: | |||||||
|     ///
 |     ///
 | ||||||
|     const Layer* FindLayer(u64 layer_id) const; |     const Layer* FindLayer(u64 layer_id) const; | ||||||
| 
 | 
 | ||||||
|  |     Nvnflinger::HardwareComposer& GetComposer() const { | ||||||
|  |         return *hardware_composer; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     u64 display_id; |     u64 display_id; | ||||||
|     std::string name; |     std::string name; | ||||||
| @ -125,6 +135,7 @@ private: | |||||||
|     KernelHelpers::ServiceContext& service_context; |     KernelHelpers::ServiceContext& service_context; | ||||||
| 
 | 
 | ||||||
|     std::vector<std::unique_ptr<Layer>> layers; |     std::vector<std::unique_ptr<Layer>> layers; | ||||||
|  |     std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer; | ||||||
|     Kernel::KEvent* vsync_event{}; |     Kernel::KEvent* vsync_event{}; | ||||||
|     bool is_abandoned{}; |     bool is_abandoned{}; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -195,8 +195,9 @@ private: | |||||||
|     void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { |     void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { | ||||||
|         IPC::RequestParser rp{ctx}; |         IPC::RequestParser rp{ctx}; | ||||||
|         const u64 buffer_id = rp.PopRaw<u64>(); |         const u64 buffer_id = rp.PopRaw<u64>(); | ||||||
|  |         const u64 aruid = ctx.GetPID(); | ||||||
| 
 | 
 | ||||||
|         LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id); |         LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid); | ||||||
| 
 | 
 | ||||||
|         struct OutputParameters { |         struct OutputParameters { | ||||||
|             s32 nvmap_handle; |             s32 nvmap_handle; | ||||||
| @ -206,7 +207,7 @@ private: | |||||||
|         OutputParameters out{}; |         OutputParameters out{}; | ||||||
|         Nvnflinger::SharedMemoryPoolLayout layout{}; |         Nvnflinger::SharedMemoryPoolLayout layout{}; | ||||||
|         const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( |         const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( | ||||||
|             &out.size, &out.nvmap_handle, &layout, buffer_id, 0); |             &out.size, &out.nvmap_handle, &layout, buffer_id, aruid); | ||||||
| 
 | 
 | ||||||
|         ctx.WriteBuffer(&layout, sizeof(layout)); |         ctx.WriteBuffer(&layout, sizeof(layout)); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | ||||||
| #include "core/hle/service/nvnflinger/pixel_format.h" | #include "core/hle/service/nvnflinger/pixel_format.h" | ||||||
|  | #include "core/hle/service/nvnflinger/ui/fence.h" | ||||||
| 
 | 
 | ||||||
| namespace Tegra { | namespace Tegra { | ||||||
| 
 | 
 | ||||||
| @ -21,7 +22,7 @@ struct FramebufferConfig { | |||||||
|     u32 stride{}; |     u32 stride{}; | ||||||
|     Service::android::PixelFormat pixel_format{}; |     Service::android::PixelFormat pixel_format{}; | ||||||
|     Service::android::BufferTransformFlags transform_flags{}; |     Service::android::BufferTransformFlags transform_flags{}; | ||||||
|     Common::Rectangle<int> crop_rect; |     Common::Rectangle<int> crop_rect{}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | ||||||
|  | |||||||
| @ -274,11 +274,6 @@ struct GPU::Impl { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Swap buffers (render frame)
 |  | ||||||
|     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |  | ||||||
|         gpu_thread.SwapBuffers(framebuffer); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
 |     /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
 | ||||||
|     void FlushRegion(DAddr addr, u64 size) { |     void FlushRegion(DAddr addr, u64 size) { | ||||||
|         gpu_thread.FlushRegion(addr, size); |         gpu_thread.FlushRegion(addr, size); | ||||||
| @ -313,8 +308,9 @@ struct GPU::Impl { | |||||||
|         gpu_thread.FlushAndInvalidateRegion(addr, size); |         gpu_thread.FlushAndInvalidateRegion(addr, size); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, |     void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, | ||||||
|                             std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { |                           std::vector<Service::Nvidia::NvFence>&& fences) { | ||||||
|  |         size_t num_fences{fences.size()}; | ||||||
|         size_t current_request_counter{}; |         size_t current_request_counter{}; | ||||||
|         { |         { | ||||||
|             std::unique_lock<std::mutex> lk(request_swap_mutex); |             std::unique_lock<std::mutex> lk(request_swap_mutex); | ||||||
| @ -328,13 +324,12 @@ struct GPU::Impl { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         const auto wait_fence = |         const auto wait_fence = | ||||||
|             RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { |             RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { | ||||||
|                 auto& syncpoint_manager = host1x.GetSyncpointManager(); |                 auto& syncpoint_manager = host1x.GetSyncpointManager(); | ||||||
|                 if (num_fences == 0) { |                 if (num_fences == 0) { | ||||||
|                     renderer->SwapBuffers(framebuffer); |                     renderer->Composite(layers); | ||||||
|                 } |                 } | ||||||
|                 const auto executer = [this, current_request_counter, |                 const auto executer = [this, current_request_counter, layers_copy = layers]() { | ||||||
|                                        framebuffer_copy = *framebuffer]() { |  | ||||||
|                     { |                     { | ||||||
|                         std::unique_lock<std::mutex> lk(request_swap_mutex); |                         std::unique_lock<std::mutex> lk(request_swap_mutex); | ||||||
|                         if (--request_swap_counters[current_request_counter] != 0) { |                         if (--request_swap_counters[current_request_counter] != 0) { | ||||||
| @ -342,7 +337,7 @@ struct GPU::Impl { | |||||||
|                         } |                         } | ||||||
|                         free_swap_counters.push_back(current_request_counter); |                         free_swap_counters.push_back(current_request_counter); | ||||||
|                     } |                     } | ||||||
|                     renderer->SwapBuffers(&framebuffer_copy); |                     renderer->Composite(layers_copy); | ||||||
|                 }; |                 }; | ||||||
|                 for (size_t i = 0; i < num_fences; i++) { |                 for (size_t i = 0; i < num_fences; i++) { | ||||||
|                     syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); |                     syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); | ||||||
| @ -505,9 +500,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { | |||||||
|     return impl->ShaderNotify(); |     return impl->ShaderNotify(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, | ||||||
|                              std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { |                            std::vector<Service::Nvidia::NvFence>&& fences) { | ||||||
|     impl->RequestSwapBuffers(framebuffer, fences, num_fences); |     impl->RequestComposite(std::move(layers), std::move(fences)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u64 GPU::GetTicks() const { | u64 GPU::GetTicks() const { | ||||||
| @ -554,10 +549,6 @@ void GPU::ClearCdmaInstance(u32 id) { | |||||||
|     impl->ClearCdmaInstance(id); |     impl->ClearCdmaInstance(id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |  | ||||||
|     impl->SwapBuffers(framebuffer); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { | VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { | ||||||
|     return impl->OnCPURead(addr, size); |     return impl->OnCPURead(addr, size); | ||||||
| } | } | ||||||
|  | |||||||
| @ -212,8 +212,8 @@ public: | |||||||
| 
 | 
 | ||||||
|     void RendererFrameEndNotify(); |     void RendererFrameEndNotify(); | ||||||
| 
 | 
 | ||||||
|     void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, |     void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, | ||||||
|                             std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences); |                           std::vector<Service::Nvidia::NvFence>&& fences); | ||||||
| 
 | 
 | ||||||
|     /// Performs any additional setup necessary in order to begin GPU emulation.
 |     /// Performs any additional setup necessary in order to begin GPU emulation.
 | ||||||
|     /// This can be used to launch any necessary threads and register any necessary
 |     /// This can be used to launch any necessary threads and register any necessary
 | ||||||
|  | |||||||
| @ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system, | |||||||
|         } |         } | ||||||
|         if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { |         if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { | ||||||
|             scheduler.Push(submit_list->channel, std::move(submit_list->entries)); |             scheduler.Push(submit_list->channel, std::move(submit_list->entries)); | ||||||
|         } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { |  | ||||||
|             renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); |  | ||||||
|         } else if (std::holds_alternative<GPUTickCommand>(next.data)) { |         } else if (std::holds_alternative<GPUTickCommand>(next.data)) { | ||||||
|             system.GPU().TickWork(); |             system.GPU().TickWork(); | ||||||
|         } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { |         } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { | ||||||
| @ -78,10 +76,6 @@ void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { | |||||||
|     PushCommand(SubmitListCommand(channel, std::move(entries))); |     PushCommand(SubmitListCommand(channel, std::move(entries))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |  | ||||||
|     PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ThreadManager::FlushRegion(DAddr addr, u64 size) { | void ThreadManager::FlushRegion(DAddr addr, u64 size) { | ||||||
|     if (!is_async) { |     if (!is_async) { | ||||||
|         // Always flush with synchronous GPU mode
 |         // Always flush with synchronous GPU mode
 | ||||||
|  | |||||||
| @ -44,14 +44,6 @@ struct SubmitListCommand final { | |||||||
|     Tegra::CommandList entries; |     Tegra::CommandList entries; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Command to signal to the GPU thread that a swap buffers is pending
 |  | ||||||
| struct SwapBuffersCommand final { |  | ||||||
|     explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_) |  | ||||||
|         : framebuffer{std::move(framebuffer_)} {} |  | ||||||
| 
 |  | ||||||
|     std::optional<Tegra::FramebufferConfig> framebuffer; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Command to signal to the GPU thread to flush a region
 | /// Command to signal to the GPU thread to flush a region
 | ||||||
| struct FlushRegionCommand final { | struct FlushRegionCommand final { | ||||||
|     explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} |     explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} | ||||||
| @ -81,8 +73,8 @@ struct FlushAndInvalidateRegionCommand final { | |||||||
| struct GPUTickCommand final {}; | struct GPUTickCommand final {}; | ||||||
| 
 | 
 | ||||||
| using CommandData = | using CommandData = | ||||||
|     std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, |     std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand, | ||||||
|                  InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>; |                  FlushAndInvalidateRegionCommand, GPUTickCommand>; | ||||||
| 
 | 
 | ||||||
| struct CommandDataContainer { | struct CommandDataContainer { | ||||||
|     CommandDataContainer() = default; |     CommandDataContainer() = default; | ||||||
| @ -118,9 +110,6 @@ public: | |||||||
|     /// Push GPU command entries to be processed
 |     /// Push GPU command entries to be processed
 | ||||||
|     void SubmitList(s32 channel, Tegra::CommandList&& entries); |     void SubmitList(s32 channel, Tegra::CommandList&& entries); | ||||||
| 
 | 
 | ||||||
|     /// Swap buffers (render frame)
 |  | ||||||
|     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); |  | ||||||
| 
 |  | ||||||
|     /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
 |     /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
 | ||||||
|     void FlushRegion(DAddr addr, u64 size); |     void FlushRegion(DAddr addr, u64 size); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ public: | |||||||
|     virtual ~RendererBase(); |     virtual ~RendererBase(); | ||||||
| 
 | 
 | ||||||
|     /// Finalize rendering the guest frame and draw into the presentation texture
 |     /// Finalize rendering the guest frame and draw into the presentation texture
 | ||||||
|     virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; |     virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; |     [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,8 +13,8 @@ RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gp | |||||||
| 
 | 
 | ||||||
| RendererNull::~RendererNull() = default; | RendererNull::~RendererNull() = default; | ||||||
| 
 | 
 | ||||||
| void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { | ||||||
|     if (!framebuffer) { |     if (framebuffers.empty()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ public: | |||||||
|                           std::unique_ptr<Core::Frontend::GraphicsContext> context); |                           std::unique_ptr<Core::Frontend::GraphicsContext> context); | ||||||
|     ~RendererNull() override; |     ~RendererNull() override; | ||||||
| 
 | 
 | ||||||
|     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |     void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; | ||||||
| 
 | 
 | ||||||
|     VideoCore::RasterizerInterface* ReadRasterizer() override { |     VideoCore::RasterizerInterface* ReadRasterizer() override { | ||||||
|         return &m_rasterizer; |         return &m_rasterizer; | ||||||
|  | |||||||
| @ -125,15 +125,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||||||
| 
 | 
 | ||||||
| RendererOpenGL::~RendererOpenGL() = default; | RendererOpenGL::~RendererOpenGL() = default; | ||||||
| 
 | 
 | ||||||
| void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { | ||||||
|     if (!framebuffer) { |     if (framebuffers.empty()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     RenderScreenshot(framebuffer); |     RenderScreenshot(framebuffers); | ||||||
| 
 | 
 | ||||||
|     state_tracker.BindFramebuffer(0); |     state_tracker.BindFramebuffer(0); | ||||||
|     blit_screen->DrawScreen(std::span(framebuffer, 1), emu_window.GetFramebufferLayout()); |     blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); | ||||||
| 
 | 
 | ||||||
|     ++m_current_frame; |     ++m_current_frame; | ||||||
| 
 | 
 | ||||||
| @ -159,7 +159,7 @@ void RendererOpenGL::AddTelemetryFields() { | |||||||
|     telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); |     telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig* framebuffer) { | void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { | ||||||
|     if (!renderer_settings.screenshot_requested) { |     if (!renderer_settings.screenshot_requested) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @ -181,7 +181,7 @@ void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig* framebuffe | |||||||
|     glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); |     glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); | ||||||
|     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); |     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); | ||||||
| 
 | 
 | ||||||
|     blit_screen->DrawScreen(std::span(framebuffer, 1), layout); |     blit_screen->DrawScreen(framebuffers, layout); | ||||||
| 
 | 
 | ||||||
|     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | ||||||
|     glPixelStorei(GL_PACK_ROW_LENGTH, 0); |     glPixelStorei(GL_PACK_ROW_LENGTH, 0); | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ public: | |||||||
|                             std::unique_ptr<Core::Frontend::GraphicsContext> context_); |                             std::unique_ptr<Core::Frontend::GraphicsContext> context_); | ||||||
|     ~RendererOpenGL() override; |     ~RendererOpenGL() override; | ||||||
| 
 | 
 | ||||||
|     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |     void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; | ||||||
| 
 | 
 | ||||||
|     VideoCore::RasterizerInterface* ReadRasterizer() override { |     VideoCore::RasterizerInterface* ReadRasterizer() override { | ||||||
|         return &rasterizer; |         return &rasterizer; | ||||||
| @ -52,7 +52,7 @@ public: | |||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void AddTelemetryFields(); |     void AddTelemetryFields(); | ||||||
|     void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); |     void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); | ||||||
| 
 | 
 | ||||||
|     Core::TelemetrySession& telemetry_session; |     Core::TelemetrySession& telemetry_session; | ||||||
|     Core::Frontend::EmuWindow& emu_window; |     Core::Frontend::EmuWindow& emu_window; | ||||||
|  | |||||||
| @ -7,6 +7,20 @@ | |||||||
| 
 | 
 | ||||||
| namespace Vulkan { | namespace Vulkan { | ||||||
| 
 | 
 | ||||||
|  | vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) { | ||||||
|  |     const VkBufferCreateInfo dst_buffer_info{ | ||||||
|  |         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||||||
|  |         .pNext = nullptr, | ||||||
|  |         .flags = 0, | ||||||
|  |         .size = size, | ||||||
|  |         .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, | ||||||
|  |         .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||||
|  |         .queueFamilyIndexCount = 0, | ||||||
|  |         .pQueueFamilyIndices = nullptr, | ||||||
|  |     }; | ||||||
|  |     return allocator.CreateBuffer(dst_buffer_info, usage); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { | ||||||
|     const VkImageCreateInfo image_ci{ |     const VkImageCreateInfo image_ci{ | ||||||
|         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||||||
| @ -96,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc | |||||||
|     scheduler.Finish(); |     scheduler.Finish(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, | ||||||
|  |                         VkExtent3D extent) { | ||||||
|  |     const VkImageMemoryBarrier read_barrier{ | ||||||
|  |         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||||
|  |         .pNext = nullptr, | ||||||
|  |         .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||||||
|  |         .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, | ||||||
|  |         .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||||
|  |         .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||||||
|  |         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||||
|  |         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||||
|  |         .image = image, | ||||||
|  |         .subresourceRange{ | ||||||
|  |             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||||
|  |             .baseMipLevel = 0, | ||||||
|  |             .levelCount = VK_REMAINING_MIP_LEVELS, | ||||||
|  |             .baseArrayLayer = 0, | ||||||
|  |             .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||||||
|  |         }, | ||||||
|  |     }; | ||||||
|  |     const VkImageMemoryBarrier image_write_barrier{ | ||||||
|  |         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||||
|  |         .pNext = nullptr, | ||||||
|  |         .srcAccessMask = 0, | ||||||
|  |         .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||||||
|  |         .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||||||
|  |         .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||||
|  |         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||||
|  |         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||||
|  |         .image = image, | ||||||
|  |         .subresourceRange{ | ||||||
|  |             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||||
|  |             .baseMipLevel = 0, | ||||||
|  |             .levelCount = VK_REMAINING_MIP_LEVELS, | ||||||
|  |             .baseArrayLayer = 0, | ||||||
|  |             .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||||||
|  |         }, | ||||||
|  |     }; | ||||||
|  |     static constexpr VkMemoryBarrier memory_write_barrier{ | ||||||
|  |         .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||||||
|  |         .pNext = nullptr, | ||||||
|  |         .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||||||
|  |         .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||||||
|  |     }; | ||||||
|  |     const VkBufferImageCopy copy{ | ||||||
|  |         .bufferOffset = 0, | ||||||
|  |         .bufferRowLength = 0, | ||||||
|  |         .bufferImageHeight = 0, | ||||||
|  |         .imageSubresource{ | ||||||
|  |             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||||
|  |             .mipLevel = 0, | ||||||
|  |             .baseArrayLayer = 0, | ||||||
|  |             .layerCount = 1, | ||||||
|  |         }, | ||||||
|  |         .imageOffset{.x = 0, .y = 0, .z = 0}, | ||||||
|  |         .imageExtent{extent}, | ||||||
|  |     }; | ||||||
|  |     cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||||||
|  |                            read_barrier); | ||||||
|  |     cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); | ||||||
|  |     cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, | ||||||
|  |                            memory_write_barrier, nullptr, image_write_barrier); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { | ||||||
|     return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ |     return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | ||||||
|         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||||||
|  | |||||||
| @ -11,12 +11,16 @@ namespace Vulkan { | |||||||
| 
 | 
 | ||||||
| #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) | #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) | ||||||
| 
 | 
 | ||||||
|  | vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage); | ||||||
|  | 
 | ||||||
| vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); | ||||||
| void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, | ||||||
|                            VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); |                            VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); | ||||||
| void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, | void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, | ||||||
|                  vk::Image& image, VkExtent2D dimensions, VkFormat format, |                  vk::Image& image, VkExtent2D dimensions, VkFormat format, | ||||||
|                  std::span<const u8> initial_contents = {}); |                  std::span<const u8> initial_contents = {}); | ||||||
|  | void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, | ||||||
|  |                         VkExtent3D extent); | ||||||
| void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); | ||||||
| 
 | 
 | ||||||
| vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); | ||||||
|  | |||||||
| @ -20,12 +20,14 @@ | |||||||
| #include "core/frontend/graphics_context.h" | #include "core/frontend/graphics_context.h" | ||||||
| #include "core/telemetry_session.h" | #include "core/telemetry_session.h" | ||||||
| #include "video_core/gpu.h" | #include "video_core/gpu.h" | ||||||
|  | #include "video_core/renderer_vulkan/present/util.h" | ||||||
| #include "video_core/renderer_vulkan/renderer_vulkan.h" | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||||||
| #include "video_core/renderer_vulkan/vk_blit_screen.h" | #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||||||
| #include "video_core/renderer_vulkan/vk_rasterizer.h" | #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||||
| #include "video_core/renderer_vulkan/vk_state_tracker.h" | #include "video_core/renderer_vulkan/vk_state_tracker.h" | ||||||
| #include "video_core/renderer_vulkan/vk_swapchain.h" | #include "video_core/renderer_vulkan/vk_swapchain.h" | ||||||
|  | #include "video_core/textures/decoders.h" | ||||||
| #include "video_core/vulkan_common/vulkan_debug_callback.h" | #include "video_core/vulkan_common/vulkan_debug_callback.h" | ||||||
| #include "video_core/vulkan_common/vulkan_device.h" | #include "video_core/vulkan_common/vulkan_device.h" | ||||||
| #include "video_core/vulkan_common/vulkan_instance.h" | #include "video_core/vulkan_common/vulkan_instance.h" | ||||||
| @ -116,18 +118,20 @@ RendererVulkan::~RendererVulkan() { | |||||||
|     void(device.GetLogical().WaitIdle()); |     void(device.GetLogical().WaitIdle()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { | ||||||
|     if (!framebuffer) { |     if (framebuffers.empty()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); |     SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); | ||||||
|  | 
 | ||||||
|     if (!render_window.IsShown()) { |     if (!render_window.IsShown()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     RenderScreenshot(framebuffer); |     RenderScreenshot(framebuffers); | ||||||
|     Frame* frame = present_manager.GetRenderFrame(); |     Frame* frame = present_manager.GetRenderFrame(); | ||||||
|     blit_swapchain.DrawToFrame(rasterizer, frame, std::span(framebuffer, 1), |     blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers, | ||||||
|                                render_window.GetFramebufferLayout(), swapchain.GetImageCount(), |                                render_window.GetFramebufferLayout(), swapchain.GetImageCount(), | ||||||
|                                swapchain.GetImageViewFormat()); |                                swapchain.GetImageViewFormat()); | ||||||
|     scheduler.Flush(*frame->render_ready); |     scheduler.Flush(*frame->render_ready); | ||||||
| @ -163,156 +167,37 @@ void RendererVulkan::Report() const { | |||||||
|     telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |     telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig* framebuffer) { | void Vulkan::RendererVulkan::RenderScreenshot( | ||||||
|  |     std::span<const Tegra::FramebufferConfig> framebuffers) { | ||||||
|     if (!renderer_settings.screenshot_requested) { |     if (!renderer_settings.screenshot_requested) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; |  | ||||||
|     auto frame = [&]() { |  | ||||||
|         vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{ |  | ||||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |  | ||||||
|             .pNext = nullptr, |  | ||||||
|             .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, |  | ||||||
|             .imageType = VK_IMAGE_TYPE_2D, |  | ||||||
|             .format = VK_FORMAT_B8G8R8A8_UNORM, |  | ||||||
|             .extent = |  | ||||||
|                 { |  | ||||||
|                     .width = layout.width, |  | ||||||
|                     .height = layout.height, |  | ||||||
|                     .depth = 1, |  | ||||||
|                 }, |  | ||||||
|             .mipLevels = 1, |  | ||||||
|             .arrayLayers = 1, |  | ||||||
|             .samples = VK_SAMPLE_COUNT_1_BIT, |  | ||||||
|             .tiling = VK_IMAGE_TILING_OPTIMAL, |  | ||||||
|             .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | |  | ||||||
|                      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |  | ||||||
|             .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |  | ||||||
|             .queueFamilyIndexCount = 0, |  | ||||||
|             .pQueueFamilyIndices = nullptr, |  | ||||||
|             .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |  | ||||||
|         }); |  | ||||||
| 
 | 
 | ||||||
|         vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ |     constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; | ||||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |     const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; | ||||||
|             .pNext = nullptr, | 
 | ||||||
|             .flags = 0, |     auto frame = [&]() { | ||||||
|             .image = *staging_image, |         Frame f{}; | ||||||
|             .viewType = VK_IMAGE_VIEW_TYPE_2D, |         f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, | ||||||
|             .format = VK_FORMAT_B8G8R8A8_UNORM, |                                      ScreenshotFormat); | ||||||
|             .components{ |         f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); | ||||||
|                 .r = VK_COMPONENT_SWIZZLE_IDENTITY, |         f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); | ||||||
|                 .g = VK_COMPONENT_SWIZZLE_IDENTITY, |         return f; | ||||||
|                 .b = VK_COMPONENT_SWIZZLE_IDENTITY, |  | ||||||
|                 .a = VK_COMPONENT_SWIZZLE_IDENTITY, |  | ||||||
|             }, |  | ||||||
|             .subresourceRange{ |  | ||||||
|                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |  | ||||||
|                 .baseMipLevel = 0, |  | ||||||
|                 .levelCount = 1, |  | ||||||
|                 .baseArrayLayer = 0, |  | ||||||
|                 .layerCount = VK_REMAINING_ARRAY_LAYERS, |  | ||||||
|             }, |  | ||||||
|         }); |  | ||||||
|         vk::Framebuffer screenshot_fb = |  | ||||||
|             blit_screenshot.CreateFramebuffer(layout, *dst_view, VK_FORMAT_B8G8R8A8_UNORM); |  | ||||||
|         return Frame{ |  | ||||||
|             .width = layout.width, |  | ||||||
|             .height = layout.height, |  | ||||||
|             .image = std::move(staging_image), |  | ||||||
|             .image_view = std::move(dst_view), |  | ||||||
|             .framebuffer = std::move(screenshot_fb), |  | ||||||
|             .cmdbuf{}, |  | ||||||
|             .render_ready{}, |  | ||||||
|             .present_done{}, |  | ||||||
|         }; |  | ||||||
|     }(); |     }(); | ||||||
| 
 | 
 | ||||||
|     blit_screenshot.DrawToFrame(rasterizer, &frame, std::span(framebuffer, 1), layout, 1, |     blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, | ||||||
|                                 VK_FORMAT_B8G8R8A8_UNORM); |                                 VK_FORMAT_B8G8R8A8_UNORM); | ||||||
| 
 | 
 | ||||||
|     const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); |     const auto dst_buffer = CreateWrappedBuffer( | ||||||
|     const VkBufferCreateInfo dst_buffer_info{ |         memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), | ||||||
|         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |         MemoryUsage::Download); | ||||||
|         .pNext = nullptr, |  | ||||||
|         .flags = 0, |  | ||||||
|         .size = buffer_size, |  | ||||||
|         .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, |  | ||||||
|         .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |  | ||||||
|         .queueFamilyIndexCount = 0, |  | ||||||
|         .pQueueFamilyIndices = nullptr, |  | ||||||
|     }; |  | ||||||
|     const vk::Buffer dst_buffer = |  | ||||||
|         memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download); |  | ||||||
| 
 | 
 | ||||||
|     scheduler.RequestOutsideRenderPassOperationContext(); |     scheduler.RequestOutsideRenderPassOperationContext(); | ||||||
|     scheduler.Record([&](vk::CommandBuffer cmdbuf) { |     scheduler.Record([&](vk::CommandBuffer cmdbuf) { | ||||||
|         const VkImageMemoryBarrier read_barrier{ |         DownloadColorImage(cmdbuf, *frame.image, *dst_buffer, | ||||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |                            VkExtent3D{layout.width, layout.height, 1}); | ||||||
|             .pNext = nullptr, |  | ||||||
|             .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, |  | ||||||
|             .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, |  | ||||||
|             .oldLayout = VK_IMAGE_LAYOUT_GENERAL, |  | ||||||
|             .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |  | ||||||
|             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |  | ||||||
|             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |  | ||||||
|             .image = *frame.image, |  | ||||||
|             .subresourceRange{ |  | ||||||
|                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |  | ||||||
|                 .baseMipLevel = 0, |  | ||||||
|                 .levelCount = VK_REMAINING_MIP_LEVELS, |  | ||||||
|                 .baseArrayLayer = 0, |  | ||||||
|                 .layerCount = VK_REMAINING_ARRAY_LAYERS, |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         const VkImageMemoryBarrier image_write_barrier{ |  | ||||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |  | ||||||
|             .pNext = nullptr, |  | ||||||
|             .srcAccessMask = 0, |  | ||||||
|             .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, |  | ||||||
|             .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |  | ||||||
|             .newLayout = VK_IMAGE_LAYOUT_GENERAL, |  | ||||||
|             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |  | ||||||
|             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |  | ||||||
|             .image = *frame.image, |  | ||||||
|             .subresourceRange{ |  | ||||||
|                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |  | ||||||
|                 .baseMipLevel = 0, |  | ||||||
|                 .levelCount = VK_REMAINING_MIP_LEVELS, |  | ||||||
|                 .baseArrayLayer = 0, |  | ||||||
|                 .layerCount = VK_REMAINING_ARRAY_LAYERS, |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         static constexpr VkMemoryBarrier memory_write_barrier{ |  | ||||||
|             .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, |  | ||||||
|             .pNext = nullptr, |  | ||||||
|             .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, |  | ||||||
|             .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, |  | ||||||
|         }; |  | ||||||
|         const VkBufferImageCopy copy{ |  | ||||||
|             .bufferOffset = 0, |  | ||||||
|             .bufferRowLength = 0, |  | ||||||
|             .bufferImageHeight = 0, |  | ||||||
|             .imageSubresource{ |  | ||||||
|                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |  | ||||||
|                 .mipLevel = 0, |  | ||||||
|                 .baseArrayLayer = 0, |  | ||||||
|                 .layerCount = 1, |  | ||||||
|             }, |  | ||||||
|             .imageOffset{.x = 0, .y = 0, .z = 0}, |  | ||||||
|             .imageExtent{ |  | ||||||
|                 .width = layout.width, |  | ||||||
|                 .height = layout.height, |  | ||||||
|                 .depth = 1, |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, |  | ||||||
|                                0, read_barrier); |  | ||||||
|         cmdbuf.CopyImageToBuffer(*frame.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer, |  | ||||||
|                                  copy); |  | ||||||
|         cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, |  | ||||||
|                                0, memory_write_barrier, nullptr, image_write_barrier); |  | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|     // Ensure the copy is fully completed before saving the screenshot
 |     // Ensure the copy is fully completed before saving the screenshot
 | ||||||
|     scheduler.Finish(); |     scheduler.Finish(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ public: | |||||||
|                             std::unique_ptr<Core::Frontend::GraphicsContext> context_); |                             std::unique_ptr<Core::Frontend::GraphicsContext> context_); | ||||||
|     ~RendererVulkan() override; |     ~RendererVulkan() override; | ||||||
| 
 | 
 | ||||||
|     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |     void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; | ||||||
| 
 | 
 | ||||||
|     VideoCore::RasterizerInterface* ReadRasterizer() override { |     VideoCore::RasterizerInterface* ReadRasterizer() override { | ||||||
|         return &rasterizer; |         return &rasterizer; | ||||||
| @ -59,7 +59,7 @@ public: | |||||||
| private: | private: | ||||||
|     void Report() const; |     void Report() const; | ||||||
| 
 | 
 | ||||||
|     void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); |     void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); | ||||||
| 
 | 
 | ||||||
|     Core::TelemetrySession& telemetry_session; |     Core::TelemetrySession& telemetry_session; | ||||||
|     Tegra::MaxwellDeviceMemoryManager& device_memory; |     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||||
|  | |||||||
| @ -115,7 +115,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, | vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, | ||||||
|                                               const VkImageView& image_view, |                                               VkImageView image_view, | ||||||
|                                               VkFormat current_view_format) { |                                               VkFormat current_view_format) { | ||||||
|     const bool format_updated = |     const bool format_updated = | ||||||
|         std::exchange(swapchain_view_format, current_view_format) != current_view_format; |         std::exchange(swapchain_view_format, current_view_format) != current_view_format; | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ public: | |||||||
|                      VkFormat current_swapchain_view_format); |                      VkFormat current_swapchain_view_format); | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, |     [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, | ||||||
|                                                     const VkImageView& image_view, |                                                     VkImageView image_view, | ||||||
|                                                     VkFormat current_view_format); |                                                     VkFormat current_view_format); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user