vi: manage resources independently of nvnflinger and refactor

This commit is contained in:
Liam
2024-02-14 23:44:05 -05:00
parent dcce9837d2
commit 812f23d05c
62 changed files with 1758 additions and 1492 deletions

View File

@@ -20,29 +20,12 @@ class HLERequestContext;
namespace Service::android {
enum class TransactionId {
RequestBuffer = 1,
SetBufferCount = 2,
DequeueBuffer = 3,
DetachBuffer = 4,
DetachNextBuffer = 5,
AttachBuffer = 6,
QueueBuffer = 7,
CancelBuffer = 8,
Query = 9,
Connect = 10,
Disconnect = 11,
AllocateBuffers = 13,
SetPreallocatedBuffer = 14,
GetBufferHistory = 17,
};
class IBinder {
public:
virtual ~IBinder() = default;
virtual void Transact(android::TransactionId code, u32 flags, std::span<const u8> parcel_data,
std::span<u8> parcel_reply) = 0;
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
virtual void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
u32 flags) = 0;
virtual Kernel::KReadableEvent* GetNativeHandle(u32 type_id) = 0;
};
} // namespace Service::android

View File

@@ -12,7 +12,7 @@
namespace Service::android {
BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
BufferItemConsumer::BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer_)
: ConsumerBase{std::move(consumer_)} {}
Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,

View File

@@ -19,7 +19,7 @@ class BufferItem;
class BufferItemConsumer final : public ConsumerBase {
public:
explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
explicit BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer);
Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
bool wait_for_fence = true);
Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence);

View File

@@ -4,12 +4,13 @@
// Parts of this implementation were based on:
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/parcel.h"
#include "core/hle/service/nvnflinger/producer_listener.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
namespace Service::android {
@@ -254,4 +255,77 @@ Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
return Status::NoError;
}
void BufferQueueConsumer::Transact(u32 code, std::span<const u8> parcel_data,
std::span<u8> parcel_reply, u32 flags) {
// Values used by BnGraphicBufferConsumer onTransact
enum class TransactionId {
AcquireBuffer = 1,
DetachBuffer = 2,
AttachBuffer = 3,
ReleaseBuffer = 4,
ConsumerConnect = 5,
ConsumerDisconnect = 6,
GetReleasedBuffers = 7,
SetDefaultBufferSize = 8,
SetDefaultMaxBufferCount = 9,
DisableAsyncBuffer = 10,
SetMaxAcquiredBufferCount = 11,
SetConsumerName = 12,
SetDefaultBufferFormat = 13,
SetConsumerUsageBits = 14,
SetTransformHint = 15,
GetSidebandStream = 16,
Unknown18 = 18,
Unknown20 = 20,
};
Status status{Status::NoError};
InputParcel parcel_in{parcel_data};
OutputParcel parcel_out{};
switch (static_cast<TransactionId>(code)) {
case TransactionId::AcquireBuffer: {
BufferItem item;
const s64 present_when = parcel_in.Read<s64>();
status = AcquireBuffer(&item, std::chrono::nanoseconds{present_when});
// TODO: can't write this directly, needs a flattener for the sp<GraphicBuffer>
// parcel_out.WriteFlattened(item);
UNREACHABLE();
}
case TransactionId::ReleaseBuffer: {
const s32 slot = parcel_in.Read<s32>();
const u64 frame_number = parcel_in.Read<u64>();
const auto release_fence = parcel_in.ReadFlattened<Fence>();
status = ReleaseBuffer(slot, frame_number, release_fence);
break;
}
case TransactionId::GetReleasedBuffers: {
u64 slot_mask = 0;
status = GetReleasedBuffers(&slot_mask);
parcel_out.Write(slot_mask);
break;
}
default:
ASSERT_MSG(false, "called, code={} flags={}", code, flags);
break;
}
parcel_out.Write(status);
const auto serialized = parcel_out.Serialize();
std::memcpy(parcel_reply.data(), serialized.data(),
std::min(parcel_reply.size(), serialized.size()));
}
Kernel::KReadableEvent* BufferQueueConsumer::GetNativeHandle(u32 type_id) {
ASSERT_MSG(false, "called, type_id={}", type_id);
return nullptr;
}
} // namespace Service::android

View File

@@ -10,6 +10,7 @@
#include <memory>
#include "common/common_types.h"
#include "core/hle/service/nvnflinger/binder.h"
#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
#include "core/hle/service/nvnflinger/status.h"
@@ -19,10 +20,10 @@ class BufferItem;
class BufferQueueCore;
class IConsumerListener;
class BufferQueueConsumer final {
class BufferQueueConsumer final : public IBinder {
public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
~BufferQueueConsumer();
~BufferQueueConsumer() override;
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
@@ -30,6 +31,11 @@ public:
Status Disconnect();
Status GetReleasedBuffers(u64* out_slot_mask);
void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
u32 flags) override;
Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override;
private:
std::shared_ptr<BufferQueueCore> core;
BufferQueueDefs::SlotsType& slots;

View File

@@ -6,12 +6,9 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
@@ -19,7 +16,6 @@
#include "core/hle/service/nvnflinger/parcel.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
#include "core/hle/service/nvnflinger/window.h"
#include "core/hle/service/vi/vi.h"
namespace Service::android {
@@ -807,13 +803,31 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
return Status::NoError;
}
void BufferQueueProducer::Transact(TransactionId code, u32 flags, std::span<const u8> parcel_data,
std::span<u8> parcel_reply) {
void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
std::span<u8> parcel_reply, u32 flags) {
// Values used by BnGraphicBufferProducer onTransact
enum class TransactionId {
RequestBuffer = 1,
SetBufferCount = 2,
DequeueBuffer = 3,
DetachBuffer = 4,
DetachNextBuffer = 5,
AttachBuffer = 6,
QueueBuffer = 7,
CancelBuffer = 8,
Query = 9,
Connect = 10,
Disconnect = 11,
AllocateBuffers = 13,
SetPreallocatedBuffer = 14,
GetBufferHistory = 17,
};
Status status{Status::NoError};
InputParcel parcel_in{parcel_data};
OutputParcel parcel_out{};
switch (code) {
switch (static_cast<TransactionId>(code)) {
case TransactionId::Connect: {
const auto enable_listener = parcel_in.Read<bool>();
const auto api = parcel_in.Read<NativeWindowApi>();
@@ -923,8 +937,8 @@ void BufferQueueProducer::Transact(TransactionId code, u32 flags, std::span<cons
std::min(parcel_reply.size(), serialized.size()));
}
Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
return buffer_wait_event->GetReadableEvent();
Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) {
return &buffer_wait_event->GetReadableEvent();
}
} // namespace Service::android

View File

@@ -45,12 +45,12 @@ public:
explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
Service::Nvidia::NvCore::NvMap& nvmap_);
~BufferQueueProducer();
~BufferQueueProducer() override;
void Transact(android::TransactionId code, u32 flags, std::span<const u8> parcel_data,
std::span<u8> parcel_reply) override;
void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
u32 flags) override;
Kernel::KReadableEvent& GetNativeHandle() override;
Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override;
public:
Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);

View File

@@ -14,7 +14,7 @@
namespace Service::android {
ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
ConsumerBase::ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_)
: consumer{std::move(consumer_)} {}
ConsumerBase::~ConsumerBase() {

View File

@@ -27,7 +27,7 @@ public:
void Abandon();
protected:
explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
explicit ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_);
~ConsumerBase() override;
void OnFrameAvailable(const BufferItem& item) override;
@@ -54,7 +54,7 @@ protected:
bool is_abandoned{};
std::unique_ptr<BufferQueueConsumer> consumer;
std::shared_ptr<BufferQueueConsumer> consumer;
mutable std::mutex mutex;
};

View File

@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
namespace Service::Nvnflinger {
struct Layer {
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
s32 consumer_id_)
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
blending(LayerBlending::None), visible(true) {}
~Layer() {
buffer_item_consumer->Abandon();
}
std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer;
s32 consumer_id;
LayerBlending blending;
bool visible;
};
struct LayerStack {
std::list<Layer> layers;
};
struct Display {
explicit Display(u64 id_) {
id = id_;
}
Layer* FindLayer(s32 consumer_id) {
for (auto& layer : stack.layers) {
if (layer.consumer_id == consumer_id) {
return &layer;
}
}
return nullptr;
}
u64 id;
LayerStack stack;
};
} // namespace Service::Nvnflinger

View File

@@ -10,8 +10,6 @@
#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 {
@@ -44,7 +42,7 @@ s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) {
HardwareComposer::HardwareComposer() = default;
HardwareComposer::~HardwareComposer() = default;
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp) {
boost::container::small_vector<HwcLayer, 2> composition_stack;
@@ -56,12 +54,11 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
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();
for (auto& layer : display.stack.layers) {
auto consumer_id = layer.consumer_id;
// Try to fetch the framebuffer (either new or stale).
const auto result = this->CacheFramebufferLocked(layer, layer_id);
const auto result = this->CacheFramebufferLocked(layer, consumer_id);
// If we failed, skip this layer.
if (result == CacheStatus::NoBufferAvailable) {
@@ -73,24 +70,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
has_acquired_buffer = true;
}
const auto& buffer = m_framebuffers[layer_id];
const auto& buffer = m_framebuffers[consumer_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,
.blending = layer.GetBlending(),
.transform = static_cast<android::BufferTransformFlags>(item.transform),
.crop_rect = item.crop,
.acquire_fence = item.fence,
});
if (layer.visible) {
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,
.blending = layer.blending,
.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.
@@ -138,7 +137,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
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());
layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
@@ -146,26 +145,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
return frame_advance;
}
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);
void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) {
// Check if we are tracking a slot with this consumer_id.
const auto it = m_framebuffers.find(consumer_id);
if (it == m_framebuffers.end()) {
return;
}
// Try to release the buffer item.
auto* const layer = display.FindLayer(layer_id);
auto* const layer = display.FindLayer(consumer_id);
if (layer && it->second.is_acquired) {
layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence());
layer->buffer_item_consumer->ReleaseBuffer(it->second.item, android::Fence::NoFence());
}
// Erase the slot.
m_framebuffers.erase(it);
}
bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) {
bool HardwareComposer::TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer) {
// Attempt the update.
const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false);
const auto status = layer.buffer_item_consumer->AcquireBuffer(&framebuffer.item, {}, false);
if (status != android::Status::NoError) {
return false;
}
@@ -178,10 +177,10 @@ bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer
return true;
}
HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer,
LayerId layer_id) {
HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(Layer& layer,
ConsumerId consumer_id) {
// Check if this framebuffer is already present.
const auto it = m_framebuffers.find(layer_id);
const auto it = m_framebuffers.find(consumer_id);
if (it != m_framebuffers.end()) {
// If it's currently still acquired, we are done.
if (it->second.is_acquired) {
@@ -203,7 +202,7 @@ HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer
if (this->TryAcquireFramebufferLocked(layer, framebuffer)) {
// Move the buffer item into a new slot.
m_framebuffers.emplace(layer_id, std::move(framebuffer));
m_framebuffers.emplace(consumer_id, std::move(framebuffer));
// We succeeded.
return CacheStatus::BufferAcquired;

View File

@@ -3,35 +3,29 @@
#pragma once
#include <memory>
#include <boost/container/flat_map.hpp>
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/display.h"
namespace Service::Nvidia::Devices {
class nvdisp_disp0;
}
namespace Service::VI {
class Display;
class Layer;
} // namespace Service::VI
namespace Service::Nvnflinger {
using LayerId = u64;
using ConsumerId = s32;
class HardwareComposer {
public:
explicit HardwareComposer();
~HardwareComposer();
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
u32 ComposeLocked(f32* out_speed_scale, Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp);
void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
void RemoveLayerLocked(Display& display, ConsumerId consumer_id);
private:
// TODO: do we want to track frame number in vi instead?
u64 m_frame_number{0};
private:
@@ -49,11 +43,11 @@ private:
CachedBufferReused,
};
boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{};
boost::container::flat_map<ConsumerId, Framebuffer> m_framebuffers{};
private:
bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer);
CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id);
bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer);
CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id);
};
} // namespace Service::Nvnflinger

View File

@@ -10,7 +10,7 @@ namespace Service::Nvnflinger {
IHOSBinderDriver::IHOSBinderDriver(Core::System& system_,
std::shared_ptr<HosBinderDriverServer> server,
std::shared_ptr<Nvnflinger> surface_flinger)
std::shared_ptr<SurfaceFlinger> surface_flinger)
: ServiceFramework{system_, "IHOSBinderDriver"}, m_server(server),
m_surface_flinger(surface_flinger) {
static const FunctionInfo functions[] = {
@@ -24,13 +24,18 @@ IHOSBinderDriver::IHOSBinderDriver(Core::System& system_,
IHOSBinderDriver::~IHOSBinderDriver() = default;
Result IHOSBinderDriver::TransactParcel(s32 binder_id, android::TransactionId transaction_id,
Result IHOSBinderDriver::TransactParcel(s32 binder_id, u32 transaction_id,
InBuffer<BufferAttr_HipcMapAlias> parcel_data,
OutBuffer<BufferAttr_HipcMapAlias> parcel_reply,
u32 flags) {
LOG_DEBUG(Service_VI, "called. id={} transaction={}, flags={}", binder_id, transaction_id,
flags);
m_server->TryGetProducer(binder_id)->Transact(transaction_id, flags, parcel_data, parcel_reply);
const auto binder = m_server->TryGetBinder(binder_id);
R_SUCCEED_IF(binder == nullptr);
binder->Transact(transaction_id, parcel_data, parcel_reply, flags);
R_SUCCEED();
}
@@ -42,11 +47,16 @@ Result IHOSBinderDriver::AdjustRefcount(s32 binder_id, s32 addval, s32 type) {
Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id,
OutCopyHandle<Kernel::KReadableEvent> out_handle) {
LOG_WARNING(Service_VI, "(STUBBED) called id={}, type_id={}", binder_id, type_id);
*out_handle = &m_server->TryGetProducer(binder_id)->GetNativeHandle();
const auto binder = m_server->TryGetBinder(binder_id);
R_UNLESS(binder != nullptr, ResultUnknown);
*out_handle = binder->GetNativeHandle(type_id);
R_SUCCEED();
}
Result IHOSBinderDriver::TransactParcelAuto(s32 binder_id, android::TransactionId transaction_id,
Result IHOSBinderDriver::TransactParcelAuto(s32 binder_id, u32 transaction_id,
InBuffer<BufferAttr_HipcAutoSelect> parcel_data,
OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply,
u32 flags) {

View File

@@ -2,38 +2,45 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/nvnflinger/binder.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KReadableEvent;
}
namespace Service::Nvnflinger {
class HosBinderDriverServer;
class Nvnflinger;
class SurfaceFlinger;
class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
public:
explicit IHOSBinderDriver(Core::System& system_, std::shared_ptr<HosBinderDriverServer> server,
std::shared_ptr<Nvnflinger> surface_flinger);
std::shared_ptr<SurfaceFlinger> surface_flinger);
~IHOSBinderDriver() override;
std::shared_ptr<Nvnflinger> GetSurfaceFlinger() {
std::shared_ptr<SurfaceFlinger> GetSurfaceFlinger() {
return m_surface_flinger;
}
std::shared_ptr<HosBinderDriverServer> GetServer() {
return m_server;
}
private:
Result TransactParcel(s32 binder_id, android::TransactionId transaction_id,
Result TransactParcel(s32 binder_id, u32 transaction_id,
InBuffer<BufferAttr_HipcMapAlias> parcel_data,
OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, u32 flags);
Result AdjustRefcount(s32 binder_id, s32 addval, s32 type);
Result GetNativeHandle(s32 binder_id, u32 type_id,
OutCopyHandle<Kernel::KReadableEvent> out_handle);
Result TransactParcelAuto(s32 binder_id, android::TransactionId transaction_id,
Result TransactParcelAuto(s32 binder_id, u32 transaction_id,
InBuffer<BufferAttr_HipcAutoSelect> parcel_data,
OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, u32 flags);
private:
const std::shared_ptr<HosBinderDriverServer> m_server;
const std::shared_ptr<Nvnflinger> m_surface_flinger;
const std::shared_ptr<SurfaceFlinger> m_surface_flinger;
};
} // namespace Service::Nvnflinger

View File

@@ -8,26 +8,30 @@
namespace Service::Nvnflinger {
HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
: service_context(system_, "HosBinderDriverServer") {}
HosBinderDriverServer::HosBinderDriverServer() = default;
HosBinderDriverServer::~HosBinderDriverServer() = default;
HosBinderDriverServer::~HosBinderDriverServer() {}
u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
s32 HosBinderDriverServer::RegisterBinder(std::shared_ptr<android::IBinder>&& binder) {
std::scoped_lock lk{lock};
last_id++;
producers[last_id] = std::move(binder);
binders[last_id] = std::move(binder);
return last_id;
}
android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
void HosBinderDriverServer::UnregisterBinder(s32 binder_id) {
std::scoped_lock lk{lock};
if (auto search = producers.find(id); search != producers.end()) {
return search->second.get();
binders.erase(binder_id);
}
std::shared_ptr<android::IBinder> HosBinderDriverServer::TryGetBinder(s32 id) const {
std::scoped_lock lk{lock};
if (auto search = binders.find(id); search != binders.end()) {
return search->second;
}
return {};

View File

@@ -8,7 +8,6 @@
#include <unordered_map>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/binder.h"
namespace Core {
@@ -19,19 +18,18 @@ namespace Service::Nvnflinger {
class HosBinderDriverServer final {
public:
explicit HosBinderDriverServer(Core::System& system_);
explicit HosBinderDriverServer();
~HosBinderDriverServer();
u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
s32 RegisterBinder(std::shared_ptr<android::IBinder>&& binder);
void UnregisterBinder(s32 binder_id);
android::IBinder* TryGetProducer(u64 id);
std::shared_ptr<android::IBinder> TryGetBinder(s32 id) const;
private:
KernelHelpers::ServiceContext service_context;
std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
std::mutex lock;
u64 last_id{};
std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders;
mutable std::mutex lock;
s32 last_id{};
};
} // namespace Service::Nvnflinger

View File

@@ -1,318 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/nvnflinger/hardware_composer.h"
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/surface_flinger.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
#include "core/hle/service/vi/vi_results.h"
namespace Service::Nvnflinger {
constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
void Nvnflinger::SplitVSync(std::stop_token stop_token) {
system.RegisterHostThread();
std::string name = "VSyncThread";
MicroProfileOnThreadCreate(name.c_str());
// Cleanup
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
while (!stop_token.stop_requested()) {
vsync_signal.Wait();
if (system.IsShuttingDown()) {
ShutdownLayers();
return;
}
const auto lock_guard = Lock();
if (!is_abandoned) {
Compose();
}
}
}
Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
: system(system_), service_context(system_, "nvnflinger"),
hos_binder_driver_server(hos_binder_driver_server_) {
displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
guard = std::make_shared<std::mutex>();
nvdrv = system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
// Schedule the screen composition events
multi_composition_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
vsync_signal.Set();
return std::chrono::nanoseconds(GetNextTicks());
});
single_composition_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto lock_guard = Lock();
Compose();
return std::chrono::nanoseconds(GetNextTicks());
});
if (system.IsMulticore()) {
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event);
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
} else {
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event);
}
}
Nvnflinger::~Nvnflinger() {
if (system.IsMulticore()) {
system.CoreTiming().UnscheduleEvent(multi_composition_event);
vsync_thread.request_stop();
vsync_signal.Set();
} else {
system.CoreTiming().UnscheduleEvent(single_composition_event);
}
ShutdownLayers();
if (nvdrv) {
nvdrv->Close(disp_fd);
}
}
void Nvnflinger::ShutdownLayers() {
// Abandon consumers.
const auto lock_guard = Lock();
for (auto& display : displays) {
display.Abandon();
}
is_abandoned = true;
}
std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) {
const auto lock_guard = Lock();
LOG_DEBUG(Service_Nvnflinger, "Opening \"{}\" display", name);
const auto itr =
std::find_if(displays.begin(), displays.end(),
[&](const VI::Display& display) { return display.GetName() == name; });
if (itr == displays.end()) {
return std::nullopt;
}
return itr->GetID();
}
bool Nvnflinger::CloseDisplay(u64 display_id) {
const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return false;
}
display->Reset();
return true;
}
std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) {
const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return std::nullopt;
}
const u64 layer_id = next_layer_id++;
CreateLayerAtId(*display, layer_id, blending);
return layer_id;
}
void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) {
const auto buffer_id = next_buffer_queue_id++;
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
display.FindLayer(layer_id)->SetBlending(blending);
}
bool Nvnflinger::OpenLayer(u64 layer_id) {
const auto lock_guard = Lock();
for (auto& display : displays) {
if (auto* layer = display.FindLayer(layer_id); layer) {
return layer->Open();
}
}
return false;
}
bool Nvnflinger::CloseLayer(u64 layer_id) {
const auto lock_guard = Lock();
for (auto& display : displays) {
if (auto* layer = display.FindLayer(layer_id); layer) {
return layer->Close();
}
}
return false;
}
void Nvnflinger::SetLayerVisibility(u64 layer_id, bool visible) {
const auto lock_guard = Lock();
for (auto& display : displays) {
if (auto* layer = display.FindLayer(layer_id); layer) {
layer->SetVisibility(visible);
}
}
}
void Nvnflinger::DestroyLayer(u64 layer_id) {
const auto lock_guard = Lock();
for (auto& display : displays) {
display.DestroyLayer(layer_id);
}
}
std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
const auto lock_guard = Lock();
const auto* const layer = FindLayer(display_id, layer_id);
if (layer == nullptr) {
return std::nullopt;
}
return layer->GetBinderId();
}
Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id) {
const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return VI::ResultNotFound;
}
*out_vsync_event = display->GetVSyncEvent();
return ResultSuccess;
}
VI::Display* Nvnflinger::FindDisplay(u64 display_id) {
const auto itr =
std::find_if(displays.begin(), displays.end(),
[&](const VI::Display& display) { return display.GetID() == display_id; });
if (itr == displays.end()) {
return nullptr;
}
return &*itr;
}
const VI::Display* Nvnflinger::FindDisplay(u64 display_id) const {
const auto itr =
std::find_if(displays.begin(), displays.end(),
[&](const VI::Display& display) { return display.GetID() == display_id; });
if (itr == displays.end()) {
return nullptr;
}
return &*itr;
}
VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) {
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return nullptr;
}
return display->FindLayer(layer_id);
}
void Nvnflinger::Compose() {
for (auto& display : displays) {
// Trigger vsync for this display at the end of drawing
SCOPE_EXIT({ display.SignalVSyncEvent(); });
// Don't do anything for displays without layers.
if (!display.HasLayers()) {
continue;
}
if (!system.IsPoweredOn()) {
return; // We are likely shutting down
}
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
}
}
s64 Nvnflinger::GetNextTicks() const {
const auto& settings = Settings::values;
auto speed_scale = 1.f;
if (settings.use_multi_core.GetValue()) {
if (settings.use_speed_limit.GetValue()) {
// Scales the speed based on speed_limit setting on MC. SC is handled by
// SpeedLimiter::DoSpeedLimiting.
speed_scale = 100.f / settings.speed_limit.GetValue();
} else {
// Run at unlocked framerate.
speed_scale = 0.01f;
}
}
// Adjust by speed limit determined during composition.
speed_scale /= compose_speed_scale;
if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
// Run at intended presentation rate during video playback.
speed_scale = 1.f;
}
const f32 effective_fps = 60.f / static_cast<f32>(swap_interval);
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
}
void LoopProcess(Core::System& system) {
const auto binder_server = std::make_shared<HosBinderDriverServer>(system);
const auto surface_flinger = std::make_shared<Nvnflinger>(system, *binder_server);
const auto binder_server = std::make_shared<HosBinderDriverServer>();
const auto surface_flinger = std::make_shared<SurfaceFlinger>(system, *binder_server);
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService(

View File

@@ -3,158 +3,12 @@
#pragma once
#include <list>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include "common/common_types.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
namespace Common {
class Event;
} // namespace Common
namespace Core::Timing {
class CoreTiming;
struct EventType;
} // namespace Core::Timing
namespace Kernel {
class KReadableEvent;
} // namespace Kernel
namespace Service::Nvidia {
class Module;
} // namespace Service::Nvidia
namespace Service::VI {
class Display;
class FbshareBufferManager;
class Layer;
} // namespace Service::VI
namespace Service::android {
class BufferQueueCore;
class BufferQueueProducer;
} // namespace Service::android
namespace Core {
class System;
}
namespace Service::Nvnflinger {
class HardwareComposer;
class HosBinderDriverServer;
class Nvnflinger final {
public:
explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
~Nvnflinger();
void ShutdownLayers();
/// Opens the specified display and returns the ID.
///
/// If an invalid display name is provided, then an empty optional is returned.
[[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
/// Closes the specified display by its ID.
///
/// Returns false if an invalid display ID is provided.
[[nodiscard]] bool CloseDisplay(u64 display_id);
/// Creates a layer on the specified display and returns the layer ID.
///
/// If an invalid display ID is specified, then an empty optional is returned.
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id,
LayerBlending blending = LayerBlending::None);
/// Opens a layer on all displays for the given layer ID.
bool OpenLayer(u64 layer_id);
/// Closes a layer on all displays for the given layer ID.
bool CloseLayer(u64 layer_id);
/// Makes a layer visible on all displays for the given layer ID.
void SetLayerVisibility(u64 layer_id, bool visible);
/// Destroys the given layer ID.
void DestroyLayer(u64 layer_id);
/// Finds the buffer queue ID of the specified layer in the specified display.
///
/// If an invalid display ID or layer ID is provided, then an empty optional is returned.
[[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id);
/// Gets the vsync event for the specified display.
///
/// If an invalid display ID is provided, then VI::ResultNotFound is returned.
/// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
[[nodiscard]] Result FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id);
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
void Compose();
[[nodiscard]] s64 GetNextTicks() const;
private:
friend class VI::FbshareBufferManager;
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
return std::unique_lock{*guard};
}
/// Finds the display identified by the specified ID.
[[nodiscard]] VI::Display* FindDisplay(u64 display_id);
/// Finds the display identified by the specified ID.
[[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
/// Finds the layer identified by the specified ID in the desired display.
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
/// Creates a layer with the specified layer ID in the desired display.
void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending);
void SplitVSync(std::stop_token stop_token);
std::shared_ptr<Nvidia::Module> nvdrv;
s32 disp_fd;
std::list<VI::Display> displays;
/// Id to use for the next layer that is created, this counter is shared among all displays.
u64 next_layer_id = 1;
/// Id to use for the next buffer queue that is created, this counter is shared among all
/// layers.
u32 next_buffer_queue_id = 1;
s32 swap_interval = 1;
f32 compose_speed_scale = 1.0f;
bool is_abandoned = false;
/// Event that handles screen composition.
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
std::shared_ptr<Core::Timing::EventType> single_composition_event;
std::shared_ptr<std::mutex> guard;
Core::System& system;
Common::Event vsync_signal;
std::jthread vsync_thread;
KernelHelpers::ServiceContext service_context;
HosBinderDriverServer& hos_binder_driver_server;
};
void LoopProcess(Core::System& system);
} // namespace Service::Nvnflinger

View File

@@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/nvnflinger/display.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/surface_flinger.h"
#include "core/hle/service/sm/sm.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_producer.h"
namespace Service::Nvnflinger {
SurfaceFlinger::SurfaceFlinger(Core::System& system, HosBinderDriverServer& server)
: m_system(system), m_server(server), m_context(m_system, "SurfaceFlinger") {
nvdrv = m_system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
}
SurfaceFlinger::~SurfaceFlinger() {
nvdrv->Close(disp_fd);
}
void SurfaceFlinger::AddDisplay(u64 display_id) {
m_displays.emplace_back(display_id);
}
void SurfaceFlinger::RemoveDisplay(u64 display_id) {
std::erase_if(m_displays, [&](auto& display) { return display.id == display_id; });
}
void SurfaceFlinger::ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
u64 display_id) {
auto* const display = this->FindDisplay(display_id);
if (!display) {
return;
}
*out_swap_interval =
m_composer.ComposeLocked(out_compose_speed_scale, *display,
*nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd));
}
void SurfaceFlinger::AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id) {
auto* const display = this->FindDisplay(display_id);
auto binder = std::static_pointer_cast<android::BufferQueueConsumer>(
m_server.TryGetBinder(consumer_binder_id));
if (!display || !binder) {
return;
}
auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(binder));
buffer_item_consumer->Connect(false);
display->stack.layers.emplace_back(std::move(buffer_item_consumer), consumer_binder_id);
}
void SurfaceFlinger::RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id) {
auto* const display = this->FindDisplay(display_id);
if (!display) {
return;
}
m_composer.RemoveLayerLocked(*display, consumer_binder_id);
std::erase_if(display->stack.layers,
[&](auto& layer) { return layer.consumer_id == consumer_binder_id; });
}
void SurfaceFlinger::SetLayerVisibility(s32 consumer_binder_id, bool visible) {
if (auto* layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
layer->visible = visible;
return;
}
}
void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blending) {
if (auto* layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
layer->blending = blending;
return;
}
}
Display* SurfaceFlinger::FindDisplay(u64 display_id) {
for (auto& display : m_displays) {
if (display.id == display_id) {
return &display;
}
}
return nullptr;
}
Layer* SurfaceFlinger::FindLayer(s32 consumer_binder_id) {
for (auto& display : m_displays) {
if (auto* layer = display.FindLayer(consumer_binder_id); layer != nullptr) {
return layer;
}
}
return nullptr;
}
void SurfaceFlinger::CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id) {
auto& nvmap = nvdrv->GetContainer().GetNvMapFile();
auto core = std::make_shared<android::BufferQueueCore>();
auto producer = std::make_shared<android::BufferQueueProducer>(m_context, core, nvmap);
auto consumer = std::make_shared<android::BufferQueueConsumer>(core);
*out_consumer_binder_id = m_server.RegisterBinder(std::move(consumer));
*out_producer_binder_id = m_server.RegisterBinder(std::move(producer));
}
void SurfaceFlinger::DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id) {
m_server.UnregisterBinder(producer_binder_id);
m_server.UnregisterBinder(consumer_binder_id);
}
} // namespace Service::Nvnflinger

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/hardware_composer.h"
namespace Core {
class System;
}
namespace Service::Nvidia {
class Module;
}
// TODO: ISurfaceComposer
// TODO: ISurfaceComposerClient
namespace Service::Nvnflinger {
struct Display;
class HosBinderDriverServer;
enum class LayerBlending : u32;
struct Layer;
class SurfaceFlinger {
public:
explicit SurfaceFlinger(Core::System& system, HosBinderDriverServer& server);
~SurfaceFlinger();
void AddDisplay(u64 display_id);
void RemoveDisplay(u64 display_id);
void ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
void AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id);
void RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id);
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
private:
Display* FindDisplay(u64 display_id);
Layer* FindLayer(s32 consumer_binder_id);
public:
// TODO: these don't belong here
void CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id);
void DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id);
private:
Core::System& m_system;
HosBinderDriverServer& m_server;
KernelHelpers::ServiceContext m_context;
std::vector<Display> m_displays;
std::shared_ptr<Nvidia::Module> nvdrv;
s32 disp_fd;
HardwareComposer m_composer;
};
} // namespace Service::Nvnflinger