diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | 35 | ||||
| -rw-r--r-- | src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | 10 | ||||
| -rw-r--r-- | src/core/hle/service/nvnflinger/hardware_composer.cpp | 215 | ||||
| -rw-r--r-- | src/core/hle/service/nvnflinger/hardware_composer.h | 59 | ||||
| -rw-r--r-- | src/core/hle/service/nvnflinger/hwc_layer.h | 27 | ||||
| -rw-r--r-- | src/core/hle/service/nvnflinger/nvnflinger.cpp | 42 | ||||
| -rw-r--r-- | src/core/hle/service/nvnflinger/nvnflinger.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/vi/display/vi_display.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/vi/display/vi_display.h | 15 | ||||
| -rw-r--r-- | src/core/hle/service/vi/vi.cpp | 5 |
11 files changed, 360 insertions, 57 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/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp new file mode 100644 index 000000000..c720dd1f8 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp | |||
| @@ -0,0 +1,215 @@ | |||
| 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 | namespace { | ||
| 20 | |||
| 21 | s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) { | ||
| 22 | if (swap_interval <= 0) { | ||
| 23 | // As an extension, treat nonpositive swap interval as speed multiplier. | ||
| 24 | if (out_speed_scale) { | ||
| 25 | *out_speed_scale = 2.f * static_cast<f32>(1 - swap_interval); | ||
| 26 | } | ||
| 27 | |||
| 28 | swap_interval = 1; | ||
| 29 | } | ||
| 30 | |||
| 31 | if (swap_interval >= 5) { | ||
| 32 | // As an extension, treat high swap interval as precise speed control. | ||
| 33 | if (out_speed_scale) { | ||
| 34 | *out_speed_scale = static_cast<f32>(swap_interval) / 100.f; | ||
| 35 | } | ||
| 36 | |||
| 37 | swap_interval = 1; | ||
| 38 | } | ||
| 39 | |||
| 40 | return swap_interval; | ||
| 41 | } | ||
| 42 | |||
| 43 | } // namespace | ||
| 44 | |||
| 45 | HardwareComposer::HardwareComposer() = default; | ||
| 46 | HardwareComposer::~HardwareComposer() = default; | ||
| 47 | |||
| 48 | u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, | ||
| 49 | Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) { | ||
| 50 | boost::container::small_vector<HwcLayer, 2> composition_stack; | ||
| 51 | |||
| 52 | m_frame_number += frame_advance; | ||
| 53 | |||
| 54 | // Release any necessary framebuffers. | ||
| 55 | for (auto& [layer_id, framebuffer] : m_framebuffers) { | ||
| 56 | if (framebuffer.release_frame_number > m_frame_number) { | ||
| 57 | // Not yet ready to release this framebuffer. | ||
| 58 | continue; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (!framebuffer.is_acquired) { | ||
| 62 | // Already released. | ||
| 63 | continue; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { | ||
| 67 | // TODO: support release fence | ||
| 68 | // This is needed to prevent screen tearing | ||
| 69 | layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); | ||
| 70 | framebuffer.is_acquired = false; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | // Set default speed limit to 100%. | ||
| 75 | *out_speed_scale = 1.0f; | ||
| 76 | |||
| 77 | // Determine the number of vsync periods to wait before composing again. | ||
| 78 | std::optional<s32> swap_interval{}; | ||
| 79 | bool has_acquired_buffer{}; | ||
| 80 | |||
| 81 | // Acquire all necessary framebuffers. | ||
| 82 | for (size_t i = 0; i < display.GetNumLayers(); i++) { | ||
| 83 | auto& layer = display.GetLayer(i); | ||
| 84 | auto layer_id = layer.GetLayerId(); | ||
| 85 | |||
| 86 | // Try to fetch the framebuffer (either new or stale). | ||
| 87 | const auto result = this->CacheFramebufferLocked(layer, layer_id); | ||
| 88 | |||
| 89 | // If we failed, skip this layer. | ||
| 90 | if (result == CacheStatus::NoBufferAvailable) { | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | |||
| 94 | // If we acquired a new buffer, we need to present. | ||
| 95 | if (result == CacheStatus::BufferAcquired) { | ||
| 96 | has_acquired_buffer = true; | ||
| 97 | } | ||
| 98 | |||
| 99 | const auto& buffer = m_framebuffers[layer_id]; | ||
| 100 | const auto& item = buffer.item; | ||
| 101 | const auto& igbp_buffer = *item.graphic_buffer; | ||
| 102 | |||
| 103 | // TODO: get proper Z-index from layer | ||
| 104 | composition_stack.emplace_back(HwcLayer{ | ||
| 105 | .buffer_handle = igbp_buffer.BufferId(), | ||
| 106 | .offset = igbp_buffer.Offset(), | ||
| 107 | .format = igbp_buffer.ExternalFormat(), | ||
| 108 | .width = igbp_buffer.Width(), | ||
| 109 | .height = igbp_buffer.Height(), | ||
| 110 | .stride = igbp_buffer.Stride(), | ||
| 111 | .z_index = 0, | ||
| 112 | .transform = static_cast<android::BufferTransformFlags>(item.transform), | ||
| 113 | .crop_rect = item.crop, | ||
| 114 | .acquire_fence = item.fence, | ||
| 115 | }); | ||
| 116 | |||
| 117 | // We need to compose again either before this frame is supposed to | ||
| 118 | // be released, or exactly on the vsync period it should be released. | ||
| 119 | const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval); | ||
| 120 | |||
| 121 | // TODO: handle cases where swap intervals are relatively prime. So far, | ||
| 122 | // only swap intervals of 0, 1 and 2 have been observed, but if 3 were | ||
| 123 | // to be introduced, this would cause an issue. | ||
| 124 | if (swap_interval) { | ||
| 125 | swap_interval = std::min(*swap_interval, item_swap_interval); | ||
| 126 | } else { | ||
| 127 | swap_interval = item_swap_interval; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | // If any new buffers were acquired, we can present. | ||
| 132 | if (has_acquired_buffer) { | ||
| 133 | // Sort by Z-index. | ||
| 134 | std::stable_sort(composition_stack.begin(), composition_stack.end(), | ||
| 135 | [&](auto& l, auto& r) { return l.z_index < r.z_index; }); | ||
| 136 | |||
| 137 | // Composite. | ||
| 138 | nvdisp.Composite(composition_stack); | ||
| 139 | } | ||
| 140 | |||
| 141 | // Render MicroProfile. | ||
| 142 | MicroProfileFlip(); | ||
| 143 | |||
| 144 | // Advance by at least one frame. | ||
| 145 | return swap_interval.value_or(1); | ||
| 146 | } | ||
| 147 | |||
| 148 | void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { | ||
| 149 | // Check if we are tracking a slot with this layer_id. | ||
| 150 | const auto it = m_framebuffers.find(layer_id); | ||
| 151 | if (it == m_framebuffers.end()) { | ||
| 152 | return; | ||
| 153 | } | ||
| 154 | |||
| 155 | // Try to release the buffer item. | ||
| 156 | auto* const layer = display.FindLayer(layer_id); | ||
| 157 | if (layer && it->second.is_acquired) { | ||
| 158 | layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); | ||
| 159 | } | ||
| 160 | |||
| 161 | // Erase the slot. | ||
| 162 | m_framebuffers.erase(it); | ||
| 163 | } | ||
| 164 | |||
| 165 | bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { | ||
| 166 | // Attempt the update. | ||
| 167 | const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); | ||
| 168 | if (status != android::Status::NoError) { | ||
| 169 | return false; | ||
| 170 | } | ||
| 171 | |||
| 172 | // We succeeded, so set the new release frame info. | ||
| 173 | framebuffer.release_frame_number = | ||
| 174 | NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval); | ||
| 175 | framebuffer.is_acquired = true; | ||
| 176 | |||
| 177 | return true; | ||
| 178 | } | ||
| 179 | |||
| 180 | HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, | ||
| 181 | LayerId layer_id) { | ||
| 182 | // Check if this framebuffer is already present. | ||
| 183 | const auto it = m_framebuffers.find(layer_id); | ||
| 184 | if (it != m_framebuffers.end()) { | ||
| 185 | // If it's currently still acquired, we are done. | ||
| 186 | if (it->second.is_acquired) { | ||
| 187 | return CacheStatus::CachedBufferReused; | ||
| 188 | } | ||
| 189 | |||
| 190 | // Try to acquire a new item. | ||
| 191 | if (this->TryAcquireFramebufferLocked(layer, it->second)) { | ||
| 192 | // We got a new item. | ||
| 193 | return CacheStatus::BufferAcquired; | ||
| 194 | } else { | ||
| 195 | // We didn't acquire a new item, but we can reuse the slot. | ||
| 196 | return CacheStatus::CachedBufferReused; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | // Framebuffer is not present, so try to create it. | ||
| 201 | Framebuffer framebuffer{}; | ||
| 202 | |||
| 203 | if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { | ||
| 204 | // Move the buffer item into a new slot. | ||
| 205 | m_framebuffers.emplace(layer_id, std::move(framebuffer)); | ||
| 206 | |||
| 207 | // We succeeded. | ||
| 208 | return CacheStatus::BufferAcquired; | ||
| 209 | } | ||
| 210 | |||
| 211 | // We couldn't acquire the buffer item, so don't create a slot. | ||
| 212 | return CacheStatus::NoBufferAvailable; | ||
| 213 | } | ||
| 214 | |||
| 215 | } // 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..ddab94ac9 --- /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(f32* out_speed_scale, VI::Display& display, | ||
| 30 | Nvidia::Devices::nvdisp_disp0& nvdisp, 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..a4e848882 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,19 @@ 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(&compose_speed_scale, display, *nvdisp, |
| 308 | static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), | 295 | swap_interval); |
| 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 | } | 296 | } |
| 322 | } | 297 | } |
| 323 | 298 | ||
| @@ -334,15 +309,16 @@ s64 Nvnflinger::GetNextTicks() const { | |||
| 334 | speed_scale = 0.01f; | 309 | speed_scale = 0.01f; |
| 335 | } | 310 | } |
| 336 | } | 311 | } |
| 312 | |||
| 313 | // Adjust by speed limit determined during composition. | ||
| 314 | speed_scale /= compose_speed_scale; | ||
| 315 | |||
| 337 | if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { | 316 | if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { |
| 338 | // Run at intended presentation rate during video playback. | 317 | // Run at intended presentation rate during video playback. |
| 339 | speed_scale = 1.f; | 318 | speed_scale = 1.f; |
| 340 | } | 319 | } |
| 341 | 320 | ||
| 342 | // As an extension, treat nonpositive swap interval as framerate multiplier. | 321 | const f32 effective_fps = 60.f / static_cast<f32>(swap_interval); |
| 343 | const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) | ||
| 344 | : 60.f / static_cast<f32>(swap_interval); | ||
| 345 | |||
| 346 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); | 322 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); |
| 347 | } | 323 | } |
| 348 | 324 | ||
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 369439142..c984d55a0 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 { |
| @@ -143,6 +144,7 @@ private: | |||
| 143 | u32 next_buffer_queue_id = 1; | 144 | u32 next_buffer_queue_id = 1; |
| 144 | 145 | ||
| 145 | s32 swap_interval = 1; | 146 | s32 swap_interval = 1; |
| 147 | f32 compose_speed_scale = 1.0f; | ||
| 146 | 148 | ||
| 147 | bool is_abandoned = false; | 149 | bool is_abandoned = false; |
| 148 | 150 | ||
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 | ||