summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/nvnflinger
diff options
context:
space:
mode:
authorLiam <byteslice@airmail.cc>2024-01-22 18:40:50 +0100
committerLiam <byteslice@airmail.cc>2024-01-31 17:27:21 +0100
commita595e9e8a7a6a742481b1cd05455d3c639095413 (patch)
tree03f8dfaec328171c42b090988e93c51e8d726eda /src/core/hle/service/nvnflinger
parentrenderer_opengl: implement layer stack composition (diff)
downloadyuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.tar
yuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.tar.gz
yuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.tar.bz2
yuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.tar.lz
yuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.tar.xz
yuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.tar.zst
yuzu-a595e9e8a7a6a742481b1cd05455d3c639095413.zip
Diffstat (limited to 'src/core/hle/service/nvnflinger')
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item.h2
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.cpp190
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.h59
-rw-r--r--src/core/hle/service/nvnflinger/hwc_layer.h27
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp32
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h1
6 files changed, 281 insertions, 30 deletions
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h
index 7fd808f54..f9f262628 100644
--- a/src/core/hle/service/nvnflinger/buffer_item.h
+++ b/src/core/hle/service/nvnflinger/buffer_item.h
@@ -40,7 +40,7 @@ public:
bool is_droppable{};
bool acquire_called{};
bool transform_to_display_inverse{};
- s32 swap_interval{};
+ u32 swap_interval{};
};
} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp
new file mode 100644
index 000000000..54889bb4f
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp
@@ -0,0 +1,190 @@
+// 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
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h
new file mode 100644
index 000000000..611afc169
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hardware_composer.h
@@ -0,0 +1,59 @@
+// 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
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 @@
+// 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
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 51133853c..e775a2ca8 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -18,6 +18,7 @@
#include "core/hle/service/nvnflinger/buffer_item_consumer.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/hardware_composer.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@@ -279,45 +280,18 @@ void Nvnflinger::Compose() {
SCOPE_EXIT({ display.SignalVSyncEvent(); });
// Don't do anything for displays without layers.
- 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) {
+ if (!display.HasLayers()) {
continue;
}
- const auto& igbp_buffer = *buffer.graphic_buffer;
-
if (!system.IsPoweredOn()) {
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);
ASSERT(nvdisp);
- Common::Rectangle<int> crop_rect{
- 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());
+ swap_interval = display.GetComposer().ComposeLocked(display, *nvdisp, swap_interval);
}
}
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 369439142..73ff36620 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -46,6 +46,7 @@ class BufferQueueProducer;
namespace Service::Nvnflinger {
class FbShareBufferManager;
+class HardwareComposer;
class HosBinderDriverServer;
class Nvnflinger final {