vi: manage resources independently of nvnflinger and refactor
This commit is contained in:
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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() {
|
||||
|
@@ -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;
|
||||
};
|
||||
|
51
src/core/hle/service/nvnflinger/display.h
Normal file
51
src/core/hle/service/nvnflinger/display.h
Normal 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
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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 {};
|
||||
|
@@ -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
|
||||
|
@@ -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(
|
||||
|
@@ -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
|
||||
|
123
src/core/hle/service/nvnflinger/surface_flinger.cpp
Normal file
123
src/core/hle/service/nvnflinger/surface_flinger.cpp
Normal 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
|
65
src/core/hle/service/nvnflinger/surface_flinger.h
Normal file
65
src/core/hle/service/nvnflinger/surface_flinger.h
Normal 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
|
Reference in New Issue
Block a user