mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-12-16 16:46:33 +00:00
vi: manage resources independently of nvnflinger and refactor
This commit is contained in:
parent
dcce9837d2
commit
812f23d05c
@ -813,6 +813,8 @@ add_library(core STATIC
|
||||
hle/service/nvnflinger/pixel_format.h
|
||||
hle/service/nvnflinger/producer_listener.h
|
||||
hle/service/nvnflinger/status.h
|
||||
hle/service/nvnflinger/surface_flinger.cpp
|
||||
hle/service/nvnflinger/surface_flinger.h
|
||||
hle/service/nvnflinger/ui/fence.h
|
||||
hle/service/nvnflinger/ui/graphic_buffer.cpp
|
||||
hle/service/nvnflinger/ui/graphic_buffer.h
|
||||
@ -906,6 +908,8 @@ add_library(core STATIC
|
||||
hle/service/server_manager.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/services.cpp
|
||||
hle/service/services.h
|
||||
hle/service/set/setting_formats/appln_settings.cpp
|
||||
hle/service/set/setting_formats/appln_settings.h
|
||||
hle/service/set/setting_formats/device_settings.cpp
|
||||
@ -953,22 +957,26 @@ add_library(core STATIC
|
||||
hle/service/ssl/ssl_backend.h
|
||||
hle/service/usb/usb.cpp
|
||||
hle/service/usb/usb.h
|
||||
hle/service/vi/display/vi_display.cpp
|
||||
hle/service/vi/display/vi_display.h
|
||||
hle/service/vi/layer/vi_layer.cpp
|
||||
hle/service/vi/layer/vi_layer.h
|
||||
hle/service/vi/application_display_service.cpp
|
||||
hle/service/vi/application_display_service.h
|
||||
hle/service/vi/application_root_service.cpp
|
||||
hle/service/vi/application_root_service.h
|
||||
hle/service/vi/fbshare_buffer_manager.cpp
|
||||
hle/service/vi/fbshare_buffer_manager.h
|
||||
hle/service/vi/conductor.cpp
|
||||
hle/service/vi/conductor.h
|
||||
hle/service/vi/container.cpp
|
||||
hle/service/vi/container.h
|
||||
hle/service/vi/display_list.h
|
||||
hle/service/vi/display.h
|
||||
hle/service/vi/layer_list.h
|
||||
hle/service/vi/layer.h
|
||||
hle/service/vi/manager_display_service.cpp
|
||||
hle/service/vi/manager_display_service.h
|
||||
hle/service/vi/manager_root_service.cpp
|
||||
hle/service/vi/manager_root_service.h
|
||||
hle/service/vi/service_creator.cpp
|
||||
hle/service/vi/service_creator.h
|
||||
hle/service/vi/shared_buffer_manager.cpp
|
||||
hle/service/vi/shared_buffer_manager.h
|
||||
hle/service/vi/system_display_service.cpp
|
||||
hle/service/vi/system_display_service.h
|
||||
hle/service/vi/system_root_service.cpp
|
||||
@ -977,6 +985,8 @@ add_library(core STATIC
|
||||
hle/service/vi/vi_types.h
|
||||
hle/service/vi/vi.cpp
|
||||
hle/service/vi/vi.h
|
||||
hle/service/vi/vsync_manager.cpp
|
||||
hle/service/vi/vsync_manager.h
|
||||
internal_network/network.cpp
|
||||
internal_network/network.h
|
||||
internal_network/network_interface.cpp
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/services.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/internal_network/network.h"
|
||||
@ -310,7 +311,8 @@ struct System::Impl {
|
||||
audio_core = std::make_unique<AudioCore::AudioCore>(system);
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||
services = std::make_unique<Service::Services>(service_manager, system);
|
||||
services =
|
||||
std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
|
||||
|
||||
is_powered_on = true;
|
||||
exit_locked = false;
|
||||
@ -458,6 +460,7 @@ struct System::Impl {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
stop_event.request_stop();
|
||||
core_timing.SyncPause(false);
|
||||
Network::CancelPendingSocketOperations();
|
||||
kernel.SuspendEmulation(true);
|
||||
@ -478,6 +481,7 @@ struct System::Impl {
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
kernel.Shutdown();
|
||||
stop_event = {};
|
||||
Network::RestartSocketOperations();
|
||||
|
||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||
@ -613,6 +617,7 @@ struct System::Impl {
|
||||
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
ExitCallback exit_callback;
|
||||
std::stop_source stop_event;
|
||||
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/display_layer_manager.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/fbshare_buffer_manager.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/manager_display_service.h"
|
||||
#include "core/hle/service/vi/manager_root_service.h"
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
@ -20,12 +21,10 @@ DisplayLayerManager::~DisplayLayerManager() {
|
||||
|
||||
void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* process,
|
||||
AppletId applet_id, LibraryAppletMode mode) {
|
||||
m_surface_flinger = system.ServiceManager()
|
||||
.GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true)
|
||||
->GetSurfaceFlinger();
|
||||
R_ASSERT(system.ServiceManager()
|
||||
.GetService<VI::IManagerRootService>("vi:m", true)
|
||||
->GetDisplayService(&m_display_service, VI::Policy::Compositor));
|
||||
R_ASSERT(m_display_service->GetManagerDisplayService(&m_manager_display_service));
|
||||
|
||||
m_process = process;
|
||||
m_system_shared_buffer_id = 0;
|
||||
@ -37,46 +36,47 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
||||
}
|
||||
|
||||
void DisplayLayerManager::Finalize() {
|
||||
if (!m_surface_flinger) {
|
||||
if (!m_manager_display_service) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up managed layers.
|
||||
for (const auto& layer : m_managed_display_layers) {
|
||||
m_surface_flinger->DestroyLayer(layer);
|
||||
m_manager_display_service->DestroyManagedLayer(layer);
|
||||
}
|
||||
|
||||
for (const auto& layer : m_managed_display_recording_layers) {
|
||||
m_surface_flinger->DestroyLayer(layer);
|
||||
m_manager_display_service->DestroyManagedLayer(layer);
|
||||
}
|
||||
|
||||
// Clean up shared layers.
|
||||
if (m_buffer_sharing_enabled) {
|
||||
m_display_service->GetSharedBufferManager()->Finalize(m_process);
|
||||
m_manager_display_service->DestroySharedLayerSession(m_process);
|
||||
}
|
||||
|
||||
m_surface_flinger = nullptr;
|
||||
m_manager_display_service = nullptr;
|
||||
m_display_service = nullptr;
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer) {
|
||||
R_UNLESS(m_surface_flinger != nullptr, VI::ResultOperationFailed);
|
||||
Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
const auto display_id = m_surface_flinger->OpenDisplay("Default");
|
||||
const auto layer_id = m_surface_flinger->CreateLayer(*display_id);
|
||||
u64 display_id;
|
||||
R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
|
||||
R_TRY(m_manager_display_service->CreateManagedLayer(
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_surface_flinger->SetLayerVisibility(*layer_id, m_visible);
|
||||
m_managed_display_layers.emplace(*layer_id);
|
||||
|
||||
*out_layer = *layer_id;
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
m_managed_display_layers.emplace(*out_layer_id);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer,
|
||||
u64* out_recording_layer) {
|
||||
R_UNLESS(m_surface_flinger != nullptr, VI::ResultOperationFailed);
|
||||
Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer_id,
|
||||
u64* out_recording_layer_id) {
|
||||
R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
@ -84,17 +84,8 @@ Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer,
|
||||
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
|
||||
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
|
||||
// side effects.
|
||||
// TODO: Support multiple layers
|
||||
const auto display_id = m_surface_flinger->OpenDisplay("Default");
|
||||
const auto layer_id = m_surface_flinger->CreateLayer(*display_id);
|
||||
|
||||
m_surface_flinger->SetLayerVisibility(*layer_id, m_visible);
|
||||
m_managed_display_layers.emplace(*layer_id);
|
||||
|
||||
*out_layer = *layer_id;
|
||||
*out_recording_layer = 0;
|
||||
|
||||
R_SUCCEED();
|
||||
*out_recording_layer_id = 0;
|
||||
R_RETURN(this->CreateManagedDisplayLayer(out_layer_id));
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
@ -102,19 +93,19 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
R_SUCCEED_IF(m_buffer_sharing_enabled);
|
||||
|
||||
// Ensure we can access shared layers.
|
||||
R_UNLESS(m_surface_flinger != nullptr, VI::ResultOperationFailed);
|
||||
R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
|
||||
R_UNLESS(m_applet_id != AppletId::Application, VI::ResultPermissionDenied);
|
||||
|
||||
// Create the shared layer.
|
||||
const auto blend =
|
||||
m_blending_enabled ? Nvnflinger::LayerBlending::Coverage : Nvnflinger::LayerBlending::None;
|
||||
const auto display_id = m_surface_flinger->OpenDisplay("Default").value();
|
||||
R_TRY(m_display_service->GetSharedBufferManager()->Initialize(
|
||||
m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blend));
|
||||
u64 display_id;
|
||||
R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
|
||||
R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id,
|
||||
&m_system_shared_layer_id, display_id,
|
||||
m_blending_enabled));
|
||||
|
||||
// We succeeded, so set up remaining state.
|
||||
m_buffer_sharing_enabled = true;
|
||||
m_surface_flinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -135,13 +126,13 @@ void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||
|
||||
m_visible = visible;
|
||||
|
||||
if (m_surface_flinger) {
|
||||
if (m_manager_display_service) {
|
||||
if (m_system_shared_layer_id) {
|
||||
m_surface_flinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
m_surface_flinger->SetLayerVisibility(layer_id, m_visible);
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,7 +144,7 @@ bool DisplayLayerManager::GetWindowVisibility() const {
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
R_RETURN(m_display_service->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index));
|
||||
}
|
||||
|
||||
|
@ -17,13 +17,10 @@ namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class Nvnflinger;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
class IApplicationDisplayService;
|
||||
}
|
||||
class IManagerDisplayService;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@ -36,8 +33,8 @@ public:
|
||||
LibraryAppletMode mode);
|
||||
void Finalize();
|
||||
|
||||
Result CreateManagedDisplayLayer(u64* out_layer);
|
||||
Result CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
|
||||
Result CreateManagedDisplayLayer(u64* out_layer_id);
|
||||
Result CreateManagedDisplaySeparableLayer(u64* out_layer_id, u64* out_recording_layer_id);
|
||||
|
||||
Result IsSystemBufferSharingEnabled();
|
||||
Result GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
|
||||
@ -50,8 +47,8 @@ public:
|
||||
|
||||
private:
|
||||
Kernel::KProcess* m_process{};
|
||||
std::shared_ptr<Nvnflinger::Nvnflinger> m_surface_flinger{};
|
||||
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
||||
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
||||
std::set<u64> m_managed_display_layers{};
|
||||
std::set<u64> m_managed_display_recording_layers{};
|
||||
u64 m_system_shared_buffer_id{};
|
||||
|
@ -10,13 +10,11 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@ -27,10 +25,6 @@ namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class Nvnflinger;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
namespace NvCore {
|
||||
@ -99,7 +93,6 @@ public:
|
||||
|
||||
private:
|
||||
friend class EventInterface;
|
||||
friend class Service::Nvnflinger::Nvnflinger;
|
||||
|
||||
/// Manages syncpoints on the host
|
||||
NvCore::Container container;
|
||||
|
@ -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
|
@ -7,67 +7,10 @@
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_server_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/bcat/bcat.h"
|
||||
#include "core/hle/service/bpc/bpc.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
#include "core/hle/service/caps/caps.h"
|
||||
#include "core/hle/service/erpt/erpt.h"
|
||||
#include "core/hle/service/es/es.h"
|
||||
#include "core/hle/service/eupld/eupld.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/fgm/fgm.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/friend/friend.h"
|
||||
#include "core/hle/service/glue/glue.h"
|
||||
#include "core/hle/service/grc/grc.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/jit/jit.h"
|
||||
#include "core/hle/service/lbl/lbl.h"
|
||||
#include "core/hle/service/ldn/ldn.h"
|
||||
#include "core/hle/service/ldr/ldr.h"
|
||||
#include "core/hle/service/lm/lm.h"
|
||||
#include "core/hle/service/mig/mig.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mm/mm_u.h"
|
||||
#include "core/hle/service/mnpp/mnpp_app.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/ngc/ngc.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/npns/npns.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/olsc/olsc.h"
|
||||
#include "core/hle/service/omm/omm.h"
|
||||
#include "core/hle/service/pcie/pcie.h"
|
||||
#include "core/hle/service/pctl/pctl_module.h"
|
||||
#include "core/hle/service/pcv/pcv.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/prepo/prepo.h"
|
||||
#include "core/hle/service/psc/psc.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
#include "core/hle/service/ro/ro.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/set/settings.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/sockets/sockets.h"
|
||||
#include "core/hle/service/spl/spl_module.h"
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
#include "core/hle/service/usb/usb.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service {
|
||||
@ -208,73 +151,4 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Initialize Services
|
||||
Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
|
||||
|
||||
// clang-format off
|
||||
kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("vi", [&] { VI::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("nvnflinger", [&] { Nvnflinger::LoopProcess(system); }).detach();
|
||||
|
||||
kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("omm", [&] { OMM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
Services::~Services() = default;
|
||||
|
||||
} // namespace Service
|
||||
|
@ -231,14 +231,4 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The purpose of this class is to own any objects that need to be shared across the other service
|
||||
* implementations. Will be torn down when the global system instance is shutdown.
|
||||
*/
|
||||
class Services final {
|
||||
public:
|
||||
explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
|
||||
~Services();
|
||||
};
|
||||
|
||||
} // namespace Service
|
||||
|
136
src/core/hle/service/services.cpp
Normal file
136
src/core/hle/service/services.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/services.h"
|
||||
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/bcat/bcat.h"
|
||||
#include "core/hle/service/bpc/bpc.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
#include "core/hle/service/caps/caps.h"
|
||||
#include "core/hle/service/erpt/erpt.h"
|
||||
#include "core/hle/service/es/es.h"
|
||||
#include "core/hle/service/eupld/eupld.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/fgm/fgm.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/friend/friend.h"
|
||||
#include "core/hle/service/glue/glue.h"
|
||||
#include "core/hle/service/grc/grc.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/jit/jit.h"
|
||||
#include "core/hle/service/lbl/lbl.h"
|
||||
#include "core/hle/service/ldn/ldn.h"
|
||||
#include "core/hle/service/ldr/ldr.h"
|
||||
#include "core/hle/service/lm/lm.h"
|
||||
#include "core/hle/service/mig/mig.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mm/mm_u.h"
|
||||
#include "core/hle/service/mnpp/mnpp_app.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/ngc/ngc.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/npns/npns.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/olsc/olsc.h"
|
||||
#include "core/hle/service/omm/omm.h"
|
||||
#include "core/hle/service/pcie/pcie.h"
|
||||
#include "core/hle/service/pctl/pctl_module.h"
|
||||
#include "core/hle/service/pcv/pcv.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/prepo/prepo.h"
|
||||
#include "core/hle/service/psc/psc.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
#include "core/hle/service/ro/ro.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/set/settings.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/sockets/sockets.h"
|
||||
#include "core/hle/service/spl/spl_module.h"
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
#include "core/hle/service/usb/usb.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||
std::stop_token token) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
|
||||
|
||||
// clang-format off
|
||||
kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
|
||||
|
||||
kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nvnflinger", [&] { Nvnflinger::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("omm", [&] { OMM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
Services::~Services() = default;
|
||||
|
||||
} // namespace Service
|
22
src/core/hle/service/services.h
Normal file
22
src/core/hle/service/services.h
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
/**
|
||||
* The purpose of this class is to own any objects that need to be shared across the other service
|
||||
* implementations. Will be torn down when the global system instance is shutdown.
|
||||
*/
|
||||
class Services final {
|
||||
public:
|
||||
explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||
std::stop_token token);
|
||||
~Services();
|
||||
};
|
||||
|
||||
} // namespace Service
|
@ -3,23 +3,20 @@
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/nvnflinger/parcel.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/manager_display_service.h"
|
||||
#include "core/hle/service/vi/system_display_service.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
IApplicationDisplayService::IApplicationDisplayService(
|
||||
Core::System& system_, std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager)
|
||||
IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
|
||||
std::shared_ptr<Container> container)
|
||||
: ServiceFramework{system_, "IApplicationDisplayService"},
|
||||
m_binder_service{std::move(binder_service)},
|
||||
m_surface_flinger{m_binder_service->GetSurfaceFlinger()},
|
||||
m_shared_buffer_manager{std::move(shared_buffer_manager)} {
|
||||
|
||||
m_container{std::move(container)}, m_context{system, "IApplicationDisplayService"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, C<&IApplicationDisplayService::GetRelayService>, "GetRelayService"},
|
||||
@ -50,39 +47,41 @@ IApplicationDisplayService::IApplicationDisplayService(
|
||||
}
|
||||
|
||||
IApplicationDisplayService::~IApplicationDisplayService() {
|
||||
for (auto& [display_id, event] : m_display_vsync_events) {
|
||||
m_container->UnlinkVsyncEvent(display_id, &event);
|
||||
}
|
||||
for (const auto layer_id : m_open_layer_ids) {
|
||||
m_container->CloseLayer(layer_id);
|
||||
}
|
||||
for (const auto layer_id : m_stray_layer_ids) {
|
||||
m_surface_flinger->DestroyLayer(layer_id);
|
||||
m_container->DestroyStrayLayer(layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::GetRelayService(
|
||||
Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
*out_relay_service = m_binder_service;
|
||||
R_SUCCEED();
|
||||
R_RETURN(m_container->GetBinderDriver(out_relay_service));
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::GetSystemDisplayService(
|
||||
Out<SharedPointer<ISystemDisplayService>> out_system_display_service) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
*out_system_display_service =
|
||||
std::make_shared<ISystemDisplayService>(system, m_surface_flinger, m_shared_buffer_manager);
|
||||
*out_system_display_service = std::make_shared<ISystemDisplayService>(system, m_container);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::GetManagerDisplayService(
|
||||
Out<SharedPointer<IManagerDisplayService>> out_manager_display_service) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
*out_manager_display_service =
|
||||
std::make_shared<IManagerDisplayService>(system, m_surface_flinger);
|
||||
*out_manager_display_service = std::make_shared<IManagerDisplayService>(system, m_container);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
|
||||
Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
*out_indirect_display_transaction_service = m_binder_service;
|
||||
R_SUCCEED();
|
||||
R_RETURN(m_container->GetBinderDriver(out_indirect_display_transaction_service));
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
|
||||
@ -92,14 +91,7 @@ Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayN
|
||||
ASSERT_MSG(strcmp(display_name.data(), "Default") == 0,
|
||||
"Non-default displays aren't supported yet");
|
||||
|
||||
const auto display_id = m_surface_flinger->OpenDisplay(display_name.data());
|
||||
if (!display_id) {
|
||||
LOG_ERROR(Service_VI, "Display not found! display_name={}", display_name.data());
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
*out_display_id = *display_id;
|
||||
R_SUCCEED();
|
||||
R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) {
|
||||
@ -109,8 +101,7 @@ Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) {
|
||||
|
||||
Result IApplicationDisplayService::CloseDisplay(u64 display_id) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_SUCCEED_IF(m_surface_flinger->CloseDisplay(display_id));
|
||||
R_THROW(ResultUnknown);
|
||||
R_RETURN(m_container->CloseDisplay(display_id));
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::SetDisplayEnabled(u32 state, u64 display_id) {
|
||||
@ -171,25 +162,19 @@ Result IApplicationDisplayService::OpenLayer(Out<u64> out_size,
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. layer_id={}, aruid={:#x}", layer_id, aruid.pid);
|
||||
|
||||
const auto display_id = m_surface_flinger->OpenDisplay(display_name.data());
|
||||
if (!display_id) {
|
||||
LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
u64 display_id;
|
||||
R_TRY(m_container->OpenDisplay(&display_id, display_name));
|
||||
|
||||
const auto buffer_queue_id = m_surface_flinger->FindBufferQueueId(*display_id, layer_id);
|
||||
if (!buffer_queue_id) {
|
||||
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
s32 producer_binder_id;
|
||||
R_TRY(m_container->OpenLayer(&producer_binder_id, layer_id, aruid.pid));
|
||||
|
||||
if (!m_surface_flinger->OpenLayer(layer_id)) {
|
||||
LOG_WARNING(Service_VI, "Tried to open layer which was already open");
|
||||
R_THROW(VI::ResultOperationFailed);
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_open_layer_ids.insert(layer_id);
|
||||
}
|
||||
|
||||
android::OutputParcel parcel;
|
||||
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
|
||||
parcel.WriteInterface(NativeWindow{producer_binder_id});
|
||||
|
||||
const auto buffer = parcel.Serialize();
|
||||
std::memcpy(out_native_window.data(), buffer.data(),
|
||||
@ -202,12 +187,13 @@ Result IApplicationDisplayService::OpenLayer(Out<u64> out_size,
|
||||
Result IApplicationDisplayService::CloseLayer(u64 layer_id) {
|
||||
LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
|
||||
|
||||
if (!m_surface_flinger->CloseLayer(layer_id)) {
|
||||
LOG_WARNING(Service_VI, "Tried to close layer which was not open");
|
||||
R_THROW(VI::ResultOperationFailed);
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_UNLESS(m_open_layer_ids.contains(layer_id), VI::ResultNotFound);
|
||||
m_open_layer_ids.erase(layer_id);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
R_RETURN(m_container->CloseLayer(layer_id));
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::CreateStrayLayer(
|
||||
@ -215,27 +201,19 @@ Result IApplicationDisplayService::CreateStrayLayer(
|
||||
u32 flags, u64 display_id) {
|
||||
LOG_DEBUG(Service_VI, "called. flags={}, display_id={}", flags, display_id);
|
||||
|
||||
const auto layer_id = m_surface_flinger->CreateLayer(display_id);
|
||||
if (!layer_id) {
|
||||
LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
s32 producer_binder_id;
|
||||
R_TRY(m_container->CreateStrayLayer(&producer_binder_id, out_layer_id, display_id));
|
||||
|
||||
m_stray_layer_ids.push_back(*layer_id);
|
||||
const auto buffer_queue_id = m_surface_flinger->FindBufferQueueId(display_id, *layer_id);
|
||||
if (!buffer_queue_id) {
|
||||
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_stray_layer_ids.insert(*out_layer_id);
|
||||
|
||||
android::OutputParcel parcel;
|
||||
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
|
||||
parcel.WriteInterface(NativeWindow{producer_binder_id});
|
||||
|
||||
const auto buffer = parcel.Serialize();
|
||||
std::memcpy(out_native_window.data(), buffer.data(),
|
||||
std::min(out_native_window.size(), buffer.size()));
|
||||
|
||||
*out_layer_id = *layer_id;
|
||||
*out_size = buffer.size();
|
||||
|
||||
R_SUCCEED();
|
||||
@ -243,25 +221,27 @@ Result IApplicationDisplayService::CreateStrayLayer(
|
||||
|
||||
Result IApplicationDisplayService::DestroyStrayLayer(u64 layer_id) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
|
||||
m_surface_flinger->DestroyLayer(layer_id);
|
||||
R_SUCCEED();
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_UNLESS(m_stray_layer_ids.contains(layer_id), VI::ResultNotFound);
|
||||
m_stray_layer_ids.erase(layer_id);
|
||||
}
|
||||
|
||||
R_RETURN(m_container->DestroyStrayLayer(layer_id));
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::GetDisplayVsyncEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, u64 display_id) {
|
||||
LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
|
||||
|
||||
const auto result = m_surface_flinger->FindVsyncEvent(out_vsync_event, display_id);
|
||||
if (result != ResultSuccess) {
|
||||
if (result == ResultNotFound) {
|
||||
LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
|
||||
}
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
R_THROW(result);
|
||||
}
|
||||
auto [it, created] = m_display_vsync_events.emplace(display_id, m_context);
|
||||
R_UNLESS(created, VI::ResultPermissionDenied);
|
||||
|
||||
R_UNLESS(!m_vsync_event_fetched, VI::ResultPermissionDenied);
|
||||
m_vsync_event_fetched = true;
|
||||
m_container->LinkVsyncEvent(display_id, &it->second);
|
||||
*out_vsync_event = it->second.GetHandle();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -1,7 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
@ -10,28 +15,25 @@ class KReadableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class Nvnflinger;
|
||||
class IHOSBinderDriver;
|
||||
} // namespace Service::Nvnflinger
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class FbshareBufferManager;
|
||||
class Container;
|
||||
class IManagerDisplayService;
|
||||
class ISystemDisplayService;
|
||||
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
public:
|
||||
IApplicationDisplayService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager);
|
||||
IApplicationDisplayService(Core::System& system_, std::shared_ptr<Container> container);
|
||||
~IApplicationDisplayService() override;
|
||||
|
||||
std::shared_ptr<FbshareBufferManager> GetSharedBufferManager() const {
|
||||
return m_shared_buffer_manager;
|
||||
std::shared_ptr<Container> GetContainer() const {
|
||||
return m_container;
|
||||
}
|
||||
|
||||
private:
|
||||
public:
|
||||
Result GetRelayService(Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service);
|
||||
Result GetSystemDisplayService(
|
||||
Out<SharedPointer<ISystemDisplayService>> out_system_display_service);
|
||||
@ -66,10 +68,13 @@ private:
|
||||
s64 width, s64 height);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_service;
|
||||
const std::shared_ptr<Nvnflinger::Nvnflinger> m_surface_flinger;
|
||||
const std::shared_ptr<FbshareBufferManager> m_shared_buffer_manager;
|
||||
std::vector<u64> m_stray_layer_ids;
|
||||
const std::shared_ptr<Container> m_container;
|
||||
|
||||
KernelHelpers::ServiceContext m_context;
|
||||
std::mutex m_lock{};
|
||||
std::set<u64> m_open_layer_ids{};
|
||||
std::set<u64> m_stray_layer_ids{};
|
||||
std::map<u64, Event> m_display_vsync_events{};
|
||||
bool m_vsync_event_fetched{false};
|
||||
};
|
||||
|
||||
|
@ -4,17 +4,16 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/application_root_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/service_creator.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
IApplicationRootService::IApplicationRootService(
|
||||
Core::System& system_, std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager)
|
||||
: ServiceFramework{system_, "vi:u"}, m_binder_service{std::move(binder_service)},
|
||||
m_shared_buffer_manager{std::move(shared_buffer_manager)} {
|
||||
IApplicationRootService::IApplicationRootService(Core::System& system_,
|
||||
std::shared_ptr<Container> container)
|
||||
: ServiceFramework{system_, "vi:u"}, m_container{std::move(container)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, C<&IApplicationRootService::GetDisplayService>, "GetDisplayService"},
|
||||
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
@ -27,8 +26,8 @@ IApplicationRootService::~IApplicationRootService() = default;
|
||||
Result IApplicationRootService::GetDisplayService(
|
||||
Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_binder_service,
|
||||
m_shared_buffer_manager, Permission::User, policy));
|
||||
R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
|
||||
Permission::User, policy));
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -10,21 +10,15 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class IHOSBinderDriver;
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class FbshareBufferManager;
|
||||
class Container;
|
||||
class IApplicationDisplayService;
|
||||
enum class Policy : u32;
|
||||
|
||||
class IApplicationRootService final : public ServiceFramework<IApplicationRootService> {
|
||||
public:
|
||||
explicit IApplicationRootService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager);
|
||||
explicit IApplicationRootService(Core::System& system_, std::shared_ptr<Container> container);
|
||||
~IApplicationRootService() override;
|
||||
|
||||
private:
|
||||
@ -33,8 +27,7 @@ private:
|
||||
Policy policy);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_service;
|
||||
const std::shared_ptr<FbshareBufferManager> m_shared_buffer_manager;
|
||||
const std::shared_ptr<Container> m_container;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
114
src/core/hle/service/vi/conductor.cpp
Normal file
114
src/core/hle/service/vi/conductor.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/vi/conductor.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/display_list.h"
|
||||
#include "core/hle/service/vi/vsync_manager.h"
|
||||
|
||||
constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60};
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
Conductor::Conductor(Core::System& system, Container& container, DisplayList& displays)
|
||||
: m_system(system), m_container(container) {
|
||||
displays.ForEachDisplay([&](Display& display) {
|
||||
m_vsync_managers.insert({display.GetId(), VsyncManager{}});
|
||||
});
|
||||
|
||||
if (system.IsMulticore()) {
|
||||
m_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
m_signal.Set();
|
||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||
});
|
||||
|
||||
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
||||
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
|
||||
} else {
|
||||
m_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
this->ProcessVsync();
|
||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||
});
|
||||
|
||||
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
||||
}
|
||||
}
|
||||
|
||||
Conductor::~Conductor() {
|
||||
m_system.CoreTiming().UnscheduleEvent(m_event);
|
||||
|
||||
if (m_system.IsMulticore()) {
|
||||
m_thread.request_stop();
|
||||
m_signal.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::LinkVsyncEvent(u64 display_id, Event* event) {
|
||||
if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
|
||||
it->second.LinkVsyncEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) {
|
||||
if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
|
||||
it->second.UnlinkVsyncEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::ProcessVsync() {
|
||||
for (auto& [display_id, manager] : m_vsync_managers) {
|
||||
m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id);
|
||||
manager.SignalVsync();
|
||||
}
|
||||
}
|
||||
|
||||
void Conductor::VsyncThread(std::stop_token token) {
|
||||
Common::SetCurrentThreadName("VSyncThread");
|
||||
|
||||
while (!token.stop_requested()) {
|
||||
m_signal.Wait();
|
||||
|
||||
if (m_system.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->ProcessVsync();
|
||||
}
|
||||
}
|
||||
|
||||
s64 Conductor::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 /= m_compose_speed_scale;
|
||||
|
||||
if (m_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>(m_swap_interval);
|
||||
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
57
src/core/hle/service/vi/conductor.h
Normal file
57
src/core/hle/service/vi/conductor.h
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class Container;
|
||||
class DisplayList;
|
||||
class VsyncManager;
|
||||
|
||||
class Conductor {
|
||||
public:
|
||||
explicit Conductor(Core::System& system, Container& container, DisplayList& displays);
|
||||
~Conductor();
|
||||
|
||||
void LinkVsyncEvent(u64 display_id, Event* event);
|
||||
void UnlinkVsyncEvent(u64 display_id, Event* event);
|
||||
|
||||
private:
|
||||
void ProcessVsync();
|
||||
void VsyncThread(std::stop_token token);
|
||||
s64 GetNextTicks() const;
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
Container& m_container;
|
||||
std::unordered_map<u64, VsyncManager> m_vsync_managers;
|
||||
std::shared_ptr<Core::Timing::EventType> m_event;
|
||||
Common::Event m_signal;
|
||||
std::jthread m_thread;
|
||||
|
||||
private:
|
||||
s32 m_swap_interval = 1;
|
||||
f32 m_compose_speed_scale = 1.0f;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
227
src/core/hle/service/vi/container.cpp
Normal file
227
src/core/hle/service/vi/container.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv_interface.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_producer.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/surface_flinger.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
Container::Container(Core::System& system) {
|
||||
m_displays.CreateDisplay(DisplayName{"Default"});
|
||||
m_displays.CreateDisplay(DisplayName{"External"});
|
||||
m_displays.CreateDisplay(DisplayName{"Edid"});
|
||||
m_displays.CreateDisplay(DisplayName{"Internal"});
|
||||
m_displays.CreateDisplay(DisplayName{"Null"});
|
||||
|
||||
m_binder_driver =
|
||||
system.ServiceManager().GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true);
|
||||
m_surface_flinger = m_binder_driver->GetSurfaceFlinger();
|
||||
|
||||
const auto nvdrv =
|
||||
system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
|
||||
m_shared_buffer_manager.emplace(system, *this, nvdrv);
|
||||
|
||||
m_displays.ForEachDisplay(
|
||||
[&](auto& display) { m_surface_flinger->AddDisplay(display.GetId()); });
|
||||
|
||||
m_conductor.emplace(system, *this, m_displays);
|
||||
}
|
||||
|
||||
Container::~Container() {
|
||||
this->OnTerminate();
|
||||
}
|
||||
|
||||
void Container::OnTerminate() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_is_shut_down = true;
|
||||
|
||||
m_layers.ForEachLayer([&](auto& layer) {
|
||||
if (layer.IsOpen()) {
|
||||
this->DestroyBufferQueueLocked(&layer);
|
||||
}
|
||||
});
|
||||
|
||||
m_displays.ForEachDisplay(
|
||||
[&](auto& display) { m_surface_flinger->RemoveDisplay(display.GetId()); });
|
||||
}
|
||||
|
||||
SharedBufferManager* Container::GetSharedBufferManager() {
|
||||
return std::addressof(*m_shared_buffer_manager);
|
||||
}
|
||||
|
||||
Result Container::GetBinderDriver(
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver) {
|
||||
*out_binder_driver = m_binder_driver;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::GetLayerProducerHandle(
|
||||
std::shared_ptr<android::BufferQueueProducer>* out_producer, u64 layer_id) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
const auto binder = m_binder_driver->GetServer()->TryGetBinder(layer->GetProducerBinderId());
|
||||
R_UNLESS(binder != nullptr, VI::ResultNotFound);
|
||||
|
||||
*out_producer = std::static_pointer_cast<android::BufferQueueProducer>(binder);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::OpenDisplay(u64* out_display_id, const DisplayName& display_name) {
|
||||
auto* const display = m_displays.GetDisplayByName(display_name);
|
||||
R_UNLESS(display != nullptr, VI::ResultNotFound);
|
||||
|
||||
*out_display_id = display->GetId();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::CloseDisplay(u64 display_id) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_RETURN(this->CreateLayerLocked(out_layer_id, display_id, owner_aruid));
|
||||
}
|
||||
|
||||
Result Container::DestroyManagedLayer(u64 layer_id) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// Try to close, if open, but don't fail if not.
|
||||
this->CloseLayerLocked(layer_id);
|
||||
|
||||
R_RETURN(this->DestroyLayerLocked(layer_id));
|
||||
}
|
||||
|
||||
Result Container::OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_RETURN(this->OpenLayerLocked(out_producer_binder_id, layer_id, aruid));
|
||||
}
|
||||
|
||||
Result Container::CloseLayer(u64 layer_id) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_RETURN(this->CloseLayerLocked(layer_id));
|
||||
}
|
||||
|
||||
Result Container::SetLayerVisibility(u64 layer_id, bool visible) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
m_surface_flinger->SetLayerVisibility(layer->GetConsumerBinderId(), visible);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
m_surface_flinger->SetLayerBlending(layer->GetConsumerBinderId(),
|
||||
enabled ? Nvnflinger::LayerBlending::Coverage
|
||||
: Nvnflinger::LayerBlending::None);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Container::LinkVsyncEvent(u64 display_id, Event* event) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_conductor->LinkVsyncEvent(display_id, event);
|
||||
}
|
||||
|
||||
void Container::UnlinkVsyncEvent(u64 display_id, Event* event) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_conductor->UnlinkVsyncEvent(display_id, event);
|
||||
}
|
||||
|
||||
Result Container::CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_TRY(this->CreateLayerLocked(out_layer_id, display_id, {}));
|
||||
R_RETURN(this->OpenLayerLocked(out_producer_binder_id, *out_layer_id, {}));
|
||||
}
|
||||
|
||||
Result Container::DestroyStrayLayer(u64 layer_id) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
R_TRY(this->CloseLayerLocked(layer_id));
|
||||
R_RETURN(this->DestroyLayerLocked(layer_id));
|
||||
}
|
||||
|
||||
Result Container::CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
|
||||
auto* const display = m_displays.GetDisplayById(display_id);
|
||||
R_UNLESS(display != nullptr, VI::ResultNotFound);
|
||||
|
||||
auto* const layer = m_layers.CreateLayer(owner_aruid, display);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
*out_layer_id = layer->GetId();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::DestroyLayerLocked(u64 layer_id) {
|
||||
R_SUCCEED_IF(m_layers.DestroyLayer(layer_id));
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
|
||||
R_UNLESS(!m_is_shut_down, VI::ResultOperationFailed);
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
R_UNLESS(!layer->IsOpen(), VI::ResultOperationFailed);
|
||||
R_UNLESS(layer->GetOwnerAruid() == aruid, VI::ResultPermissionDenied);
|
||||
|
||||
this->CreateBufferQueueLocked(layer);
|
||||
*out_producer_binder_id = layer->GetProducerBinderId();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::CloseLayerLocked(u64 layer_id) {
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
R_UNLESS(layer->IsOpen(), VI::ResultOperationFailed);
|
||||
|
||||
this->DestroyBufferQueueLocked(layer);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Container::CreateBufferQueueLocked(Layer* layer) {
|
||||
s32 consumer_binder_id, producer_binder_id;
|
||||
m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id);
|
||||
layer->Open(consumer_binder_id, producer_binder_id);
|
||||
|
||||
if (auto* display = layer->GetDisplay(); display != nullptr) {
|
||||
m_surface_flinger->AddLayerToDisplayStack(display->GetId(), consumer_binder_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::DestroyBufferQueueLocked(Layer* layer) {
|
||||
if (auto* display = layer->GetDisplay(); display != nullptr) {
|
||||
m_surface_flinger->RemoveLayerFromDisplayStack(display->GetId(),
|
||||
layer->GetConsumerBinderId());
|
||||
}
|
||||
|
||||
layer->Close();
|
||||
m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(),
|
||||
layer->GetProducerBinderId());
|
||||
}
|
||||
|
||||
void Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
|
||||
u64 display_id) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_surface_flinger->ComposeDisplay(out_swap_interval, out_compose_speed_scale, display_id);
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
92
src/core/hle/service/vi/container.h
Normal file
92
src/core/hle/service/vi/container.h
Normal file
@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/service/vi/conductor.h"
|
||||
#include "core/hle/service/vi/display_list.h"
|
||||
#include "core/hle/service/vi/layer_list.h"
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
|
||||
union Result;
|
||||
|
||||
namespace Service::android {
|
||||
class BufferQueueProducer;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class IHOSBinderDriver;
|
||||
class SurfaceFlinger;
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
namespace Service {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class SharedBufferManager;
|
||||
|
||||
class Container {
|
||||
public:
|
||||
explicit Container(Core::System& system);
|
||||
~Container();
|
||||
|
||||
void OnTerminate();
|
||||
|
||||
SharedBufferManager* GetSharedBufferManager();
|
||||
|
||||
Result GetBinderDriver(std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver);
|
||||
Result GetLayerProducerHandle(std::shared_ptr<android::BufferQueueProducer>* out_producer,
|
||||
u64 layer_id);
|
||||
|
||||
Result OpenDisplay(u64* out_display_id, const DisplayName& display_name);
|
||||
Result CloseDisplay(u64 display_id);
|
||||
|
||||
// Managed layers are created by the interaction between am and ommdisp
|
||||
// on behalf of an applet. Their lifetime ends with the lifetime of the
|
||||
// applet's ISelfController.
|
||||
Result CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid);
|
||||
Result DestroyManagedLayer(u64 layer_id);
|
||||
Result OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
|
||||
Result CloseLayer(u64 layer_id);
|
||||
|
||||
// Stray layers are created by non-applet sysmodules. Their lifetime ends
|
||||
// with the lifetime of the IApplicationDisplayService which created them.
|
||||
Result CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id);
|
||||
Result DestroyStrayLayer(u64 layer_id);
|
||||
|
||||
Result SetLayerVisibility(u64 layer_id, bool visible);
|
||||
Result SetLayerBlending(u64 layer_id, bool enabled);
|
||||
|
||||
void LinkVsyncEvent(u64 display_id, Event* event);
|
||||
void UnlinkVsyncEvent(u64 display_id, Event* event);
|
||||
|
||||
private:
|
||||
Result CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid);
|
||||
Result DestroyLayerLocked(u64 layer_id);
|
||||
Result OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
|
||||
Result CloseLayerLocked(u64 layer_id);
|
||||
|
||||
void CreateBufferQueueLocked(Layer* layer);
|
||||
void DestroyBufferQueueLocked(Layer* layer);
|
||||
|
||||
public:
|
||||
void ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
|
||||
|
||||
private:
|
||||
std::mutex m_lock{};
|
||||
DisplayList m_displays{};
|
||||
LayerList m_layers{};
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_driver{};
|
||||
std::shared_ptr<Nvnflinger::SurfaceFlinger> m_surface_flinger{};
|
||||
std::optional<SharedBufferManager> m_shared_buffer_manager{};
|
||||
std::optional<Conductor> m_conductor{};
|
||||
bool m_is_shut_down{};
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
44
src/core/hle/service/vi/display.h
Normal file
44
src/core/hle/service/vi/display.h
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class Display {
|
||||
public:
|
||||
constexpr Display() = default;
|
||||
|
||||
void Initialize(u64 id, const DisplayName& display_name) {
|
||||
m_id = id;
|
||||
m_display_name = display_name;
|
||||
m_is_initialized = true;
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
m_id = {};
|
||||
m_display_name = {};
|
||||
m_is_initialized = {};
|
||||
}
|
||||
|
||||
u64 GetId() const {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const DisplayName& GetDisplayName() const {
|
||||
return m_display_name;
|
||||
}
|
||||
|
||||
bool IsInitialized() const {
|
||||
return m_is_initialized;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 m_id{};
|
||||
DisplayName m_display_name{};
|
||||
bool m_is_initialized{};
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
@ -1,143 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
||||
#include "core/hle/service/nvnflinger/hardware_composer.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
||||
#include "core/hle/service/vi/display/vi_display.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
struct BufferQueue {
|
||||
std::shared_ptr<android::BufferQueueCore> core;
|
||||
std::unique_ptr<android::BufferQueueProducer> producer;
|
||||
std::unique_ptr<android::BufferQueueConsumer> consumer;
|
||||
};
|
||||
|
||||
static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap) {
|
||||
auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
|
||||
return {
|
||||
buffer_queue_core,
|
||||
std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
|
||||
std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
|
||||
}
|
||||
|
||||
Display::Display(u64 id, std::string name_,
|
||||
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_,
|
||||
KernelHelpers::ServiceContext& service_context_, Core::System& system_)
|
||||
: display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
|
||||
service_context{service_context_} {
|
||||
hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>();
|
||||
vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
Display::~Display() {
|
||||
service_context.CloseEvent(vsync_event);
|
||||
}
|
||||
|
||||
Layer& Display::GetLayer(std::size_t index) {
|
||||
size_t i = 0;
|
||||
for (auto& layer : layers) {
|
||||
if (!layer->IsOpen() || !layer->IsVisible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == index) {
|
||||
return *layer;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
size_t Display::GetNumLayers() const {
|
||||
return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen() && l->IsVisible(); });
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent* Display::GetVSyncEvent() {
|
||||
return &vsync_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
void Display::SignalVSyncEvent() {
|
||||
vsync_event->Signal();
|
||||
}
|
||||
|
||||
void Display::CreateLayer(u64 layer_id, u32 binder_id,
|
||||
Service::Nvidia::NvCore::Container& nv_core) {
|
||||
auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
|
||||
|
||||
auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
|
||||
buffer_item_consumer->Connect(false);
|
||||
|
||||
layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
|
||||
std::move(buffer_item_consumer)));
|
||||
|
||||
if (is_abandoned) {
|
||||
this->FindLayer(layer_id)->GetConsumer().Abandon();
|
||||
}
|
||||
|
||||
hos_binder_driver_server.RegisterProducer(std::move(producer));
|
||||
}
|
||||
|
||||
void Display::DestroyLayer(u64 layer_id) {
|
||||
if (auto* layer = this->FindLayer(layer_id); layer != nullptr) {
|
||||
layer->GetConsumer().Abandon();
|
||||
}
|
||||
|
||||
std::erase_if(layers,
|
||||
[layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
|
||||
}
|
||||
|
||||
void Display::Abandon() {
|
||||
for (auto& layer : layers) {
|
||||
layer->GetConsumer().Abandon();
|
||||
}
|
||||
is_abandoned = true;
|
||||
}
|
||||
|
||||
Layer* Display::FindLayer(u64 layer_id) {
|
||||
const auto itr =
|
||||
std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
|
||||
return layer->GetLayerId() == layer_id;
|
||||
});
|
||||
|
||||
if (itr == layers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return itr->get();
|
||||
}
|
||||
|
||||
const Layer* Display::FindLayer(u64 layer_id) const {
|
||||
const auto itr =
|
||||
std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
|
||||
return layer->GetLayerId() == layer_id;
|
||||
});
|
||||
|
||||
if (itr == layers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return itr->get();
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
@ -1,143 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::android {
|
||||
class BufferQueueProducer;
|
||||
}
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class HardwareComposer;
|
||||
class HosBinderDriverServer;
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class Container;
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class Layer;
|
||||
|
||||
/// Represents a single display type
|
||||
class Display {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Display);
|
||||
YUZU_NON_MOVEABLE(Display);
|
||||
|
||||
/// Constructs a display with a given unique ID and name.
|
||||
///
|
||||
/// @param id The unique ID for this display.
|
||||
/// @param hos_binder_driver_server_ Nvnflinger HOSBinderDriver server instance.
|
||||
/// @param service_context_ The ServiceContext for the owning service.
|
||||
/// @param name_ The name for this display.
|
||||
/// @param system_ The global system instance.
|
||||
///
|
||||
Display(u64 id, std::string name_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_,
|
||||
KernelHelpers::ServiceContext& service_context_, Core::System& system_);
|
||||
~Display();
|
||||
|
||||
/// Gets the unique ID assigned to this display.
|
||||
u64 GetID() const {
|
||||
return display_id;
|
||||
}
|
||||
|
||||
/// Gets the name of this display
|
||||
const std::string& GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Whether or not this display has any layers added to it.
|
||||
bool HasLayers() const {
|
||||
return GetNumLayers() > 0;
|
||||
}
|
||||
|
||||
/// Gets a layer for this display based off an index.
|
||||
Layer& GetLayer(std::size_t index);
|
||||
|
||||
std::size_t GetNumLayers() const;
|
||||
|
||||
/// Gets the internal vsync event.
|
||||
Kernel::KReadableEvent* GetVSyncEvent();
|
||||
|
||||
/// Signals the internal vsync event.
|
||||
void SignalVSyncEvent();
|
||||
|
||||
/// Creates and adds a layer to this display with the given ID.
|
||||
///
|
||||
/// @param layer_id The ID to assign to the created layer.
|
||||
/// @param binder_id The ID assigned to the buffer queue.
|
||||
///
|
||||
void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
|
||||
|
||||
/// Removes a layer from this display with the given ID.
|
||||
///
|
||||
/// @param layer_id The ID assigned to the layer to destroy.
|
||||
///
|
||||
void DestroyLayer(u64 layer_id);
|
||||
|
||||
/// Resets the display for a new connection.
|
||||
void Reset() {
|
||||
layers.clear();
|
||||
}
|
||||
|
||||
void Abandon();
|
||||
|
||||
/// Attempts to find a layer with the given ID.
|
||||
///
|
||||
/// @param layer_id The layer ID.
|
||||
///
|
||||
/// @returns If found, the Layer instance with the given ID.
|
||||
/// If not found, then nullptr is returned.
|
||||
///
|
||||
Layer* FindLayer(u64 layer_id);
|
||||
|
||||
/// Attempts to find a layer with the given ID.
|
||||
///
|
||||
/// @param layer_id The layer ID.
|
||||
///
|
||||
/// @returns If found, the Layer instance with the given ID.
|
||||
/// If not found, then nullptr is returned.
|
||||
///
|
||||
const Layer* FindLayer(u64 layer_id) const;
|
||||
|
||||
Nvnflinger::HardwareComposer& GetComposer() const {
|
||||
return *hardware_composer;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 display_id;
|
||||
std::string name;
|
||||
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::vector<std::unique_ptr<Layer>> layers;
|
||||
std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer;
|
||||
Kernel::KEvent* vsync_event{};
|
||||
bool is_abandoned{};
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
83
src/core/hle/service/vi/display_list.h
Normal file
83
src/core/hle/service/vi/display_list.h
Normal file
@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "core/hle/service/vi/display.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class DisplayList {
|
||||
public:
|
||||
constexpr DisplayList() = default;
|
||||
|
||||
bool CreateDisplay(const DisplayName& name) {
|
||||
Display* const display = this->GetFreeDisplay();
|
||||
if (!display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
display->Initialize(m_next_id++, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DestroyDisplay(u64 display_id) {
|
||||
Display* display = this->GetDisplayById(display_id);
|
||||
if (!display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
display->Finalize();
|
||||
return true;
|
||||
}
|
||||
|
||||
Display* GetDisplayByName(const DisplayName& name) {
|
||||
for (auto& display : m_displays) {
|
||||
if (display.IsInitialized() &&
|
||||
std::strncmp(name.data(), display.GetDisplayName().data(), sizeof(DisplayName)) ==
|
||||
0) {
|
||||
return &display;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Display* GetDisplayById(u64 display_id) {
|
||||
for (auto& display : m_displays) {
|
||||
if (display.IsInitialized() && display.GetId() == display_id) {
|
||||
return &display;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachDisplay(F&& cb) {
|
||||
for (auto& display : m_displays) {
|
||||
if (display.IsInitialized()) {
|
||||
cb(display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Display* GetFreeDisplay() {
|
||||
for (auto& display : m_displays) {
|
||||
if (!display.IsInitialized()) {
|
||||
return &display;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Display, 8> m_displays{};
|
||||
u64 m_next_id{};
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
79
src/core/hle/service/vi/layer.h
Normal file
79
src/core/hle/service/vi/layer.h
Normal file
@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class Display;
|
||||
|
||||
class Layer {
|
||||
public:
|
||||
constexpr Layer() = default;
|
||||
|
||||
void Initialize(u64 id, u64 owner_aruid, Display* display) {
|
||||
m_id = id;
|
||||
m_owner_aruid = owner_aruid;
|
||||
m_display = display;
|
||||
m_is_initialized = true;
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
m_id = {};
|
||||
m_display = {};
|
||||
m_is_initialized = {};
|
||||
}
|
||||
|
||||
void Open(s32 consumer_binder_id, s32 producer_binder_id) {
|
||||
m_consumer_binder_id = consumer_binder_id;
|
||||
m_producer_binder_id = producer_binder_id;
|
||||
m_is_open = true;
|
||||
}
|
||||
|
||||
void Close() {
|
||||
m_producer_binder_id = {};
|
||||
m_consumer_binder_id = {};
|
||||
m_is_open = {};
|
||||
}
|
||||
|
||||
u64 GetId() const {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
u64 GetOwnerAruid() const {
|
||||
return m_owner_aruid;
|
||||
}
|
||||
|
||||
Display* GetDisplay() const {
|
||||
return m_display;
|
||||
}
|
||||
|
||||
s32 GetConsumerBinderId() const {
|
||||
return m_consumer_binder_id;
|
||||
}
|
||||
|
||||
s32 GetProducerBinderId() const {
|
||||
return m_producer_binder_id;
|
||||
}
|
||||
|
||||
bool IsInitialized() const {
|
||||
return m_is_initialized;
|
||||
}
|
||||
|
||||
bool IsOpen() const {
|
||||
return m_is_open;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 m_id{};
|
||||
u64 m_owner_aruid{};
|
||||
Display* m_display{};
|
||||
s32 m_consumer_binder_id{};
|
||||
s32 m_producer_binder_id{};
|
||||
bool m_is_initialized{};
|
||||
bool m_is_open{};
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
@ -1,18 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
|
||||
android::BufferQueueProducer& binder_,
|
||||
std::shared_ptr<android::BufferItemConsumer>&& consumer_)
|
||||
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
|
||||
consumer_)},
|
||||
blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
|
||||
|
||||
Layer::~Layer() = default;
|
||||
|
||||
} // namespace Service::VI
|
@ -1,118 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
class BufferItemConsumer;
|
||||
class BufferQueueCore;
|
||||
class BufferQueueProducer;
|
||||
} // namespace Service::android
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
enum class LayerBlending : u32;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
/// Represents a single display layer.
|
||||
class Layer {
|
||||
public:
|
||||
/// Constructs a layer with a given ID and buffer queue.
|
||||
///
|
||||
/// @param layer_id_ The ID to assign to this layer.
|
||||
/// @param binder_id_ The binder ID to assign to this layer.
|
||||
/// @param binder_ The buffer producer queue for this layer to use.
|
||||
///
|
||||
Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
|
||||
android::BufferQueueProducer& binder_,
|
||||
std::shared_ptr<android::BufferItemConsumer>&& consumer_);
|
||||
~Layer();
|
||||
|
||||
Layer(const Layer&) = delete;
|
||||
Layer& operator=(const Layer&) = delete;
|
||||
|
||||
Layer(Layer&&) = default;
|
||||
Layer& operator=(Layer&&) = delete;
|
||||
|
||||
/// Gets the ID for this layer.
|
||||
u64 GetLayerId() const {
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
/// Gets the binder ID for this layer.
|
||||
u32 GetBinderId() const {
|
||||
return binder_id;
|
||||
}
|
||||
|
||||
/// Gets a reference to the buffer queue this layer is using.
|
||||
android::BufferQueueProducer& GetBufferQueue() {
|
||||
return binder;
|
||||
}
|
||||
|
||||
/// Gets a const reference to the buffer queue this layer is using.
|
||||
const android::BufferQueueProducer& GetBufferQueue() const {
|
||||
return binder;
|
||||
}
|
||||
|
||||
android::BufferItemConsumer& GetConsumer() {
|
||||
return *consumer;
|
||||
}
|
||||
|
||||
const android::BufferItemConsumer& GetConsumer() const {
|
||||
return *consumer;
|
||||
}
|
||||
|
||||
android::BufferQueueCore& Core() {
|
||||
return core;
|
||||
}
|
||||
|
||||
const android::BufferQueueCore& Core() const {
|
||||
return core;
|
||||
}
|
||||
|
||||
bool IsVisible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
void SetVisibility(bool v) {
|
||||
visible = v;
|
||||
}
|
||||
|
||||
bool IsOpen() const {
|
||||
return open;
|
||||
}
|
||||
|
||||
bool Close() {
|
||||
return std::exchange(open, false);
|
||||
}
|
||||
|
||||
bool Open() {
|
||||
return !std::exchange(open, true);
|
||||
}
|
||||
|
||||
Nvnflinger::LayerBlending GetBlending() {
|
||||
return blending;
|
||||
}
|
||||
|
||||
void SetBlending(Nvnflinger::LayerBlending b) {
|
||||
blending = b;
|
||||
}
|
||||
|
||||
private:
|
||||
const u64 layer_id;
|
||||
const u32 binder_id;
|
||||
android::BufferQueueCore& core;
|
||||
android::BufferQueueProducer& binder;
|
||||
std::shared_ptr<android::BufferItemConsumer> consumer;
|
||||
Service::Nvnflinger::LayerBlending blending;
|
||||
bool open;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
69
src/core/hle/service/vi/layer_list.h
Normal file
69
src/core/hle/service/vi/layer_list.h
Normal file
@ -0,0 +1,69 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/vi/layer.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class LayerList {
|
||||
public:
|
||||
constexpr LayerList() = default;
|
||||
|
||||
Layer* CreateLayer(u64 owner_aruid, Display* display) {
|
||||
Layer* const layer = GetFreeLayer();
|
||||
if (!layer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
layer->Initialize(++m_next_id, owner_aruid, display);
|
||||
return layer;
|
||||
}
|
||||
|
||||
bool DestroyLayer(u64 layer_id) {
|
||||
Layer* const layer = GetLayerById(layer_id);
|
||||
if (!layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
layer->Finalize();
|
||||
return true;
|
||||
}
|
||||
|
||||
Layer* GetLayerById(u64 layer_id) {
|
||||
for (auto& layer : m_layers) {
|
||||
if (layer.IsInitialized() && layer.GetId() == layer_id) {
|
||||
return &layer;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachLayer(F&& cb) {
|
||||
for (auto& layer : m_layers) {
|
||||
if (layer.IsInitialized()) {
|
||||
cb(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Layer* GetFreeLayer() {
|
||||
for (auto& layer : m_layers) {
|
||||
if (!layer.IsInitialized()) {
|
||||
return &layer;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Layer, 8> m_layers{};
|
||||
u64 m_next_id{};
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
@ -2,23 +2,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/manager_display_service.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
IManagerDisplayService::IManagerDisplayService(
|
||||
Core::System& system_, std::shared_ptr<Nvnflinger::Nvnflinger> surface_flinger)
|
||||
: ServiceFramework{system_, "IManagerDisplayService"},
|
||||
m_surface_flinger{std::move(surface_flinger)} {
|
||||
IManagerDisplayService::IManagerDisplayService(Core::System& system_,
|
||||
std::shared_ptr<Container> container)
|
||||
: ServiceFramework{system_, "IManagerDisplayService"}, m_container{std::move(container)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{200, nullptr, "AllocateProcessHeapBlock"},
|
||||
{201, nullptr, "FreeProcessHeapBlock"},
|
||||
{1102, nullptr, "GetDisplayResolution"},
|
||||
{2010, C<&IManagerDisplayService::CreateManagedLayer>, "CreateManagedLayer"},
|
||||
{2011, nullptr, "DestroyManagedLayer"},
|
||||
{2011, C<&IManagerDisplayService::DestroyManagedLayer>, "DestroyManagedLayer"},
|
||||
{2012, nullptr, "CreateStrayLayer"},
|
||||
{2050, nullptr, "CreateIndirectLayer"},
|
||||
{2051, nullptr, "DestroyIndirectLayer"},
|
||||
@ -103,19 +101,30 @@ IManagerDisplayService::IManagerDisplayService(
|
||||
|
||||
IManagerDisplayService::~IManagerDisplayService() = default;
|
||||
|
||||
Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 unknown,
|
||||
u64 display_id, AppletResourceUserId aruid) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. unknown={}, display={}, aruid={}", unknown,
|
||||
display_id, aruid.pid);
|
||||
Result IManagerDisplayService::CreateSharedLayerSession(Kernel::KProcess* owner_process,
|
||||
u64* out_buffer_id, u64* out_layer_handle,
|
||||
u64 display_id, bool enable_blending) {
|
||||
R_RETURN(m_container->GetSharedBufferManager()->CreateSession(
|
||||
owner_process, out_buffer_id, out_layer_handle, display_id, enable_blending));
|
||||
}
|
||||
|
||||
const auto layer_id = m_surface_flinger->CreateLayer(display_id);
|
||||
if (!layer_id) {
|
||||
LOG_ERROR(Service_VI, "Layer not found! display={}", display_id);
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
void IManagerDisplayService::DestroySharedLayerSession(Kernel::KProcess* owner_process) {
|
||||
m_container->GetSharedBufferManager()->DestroySession(owner_process);
|
||||
}
|
||||
|
||||
*out_layer_id = *layer_id;
|
||||
R_SUCCEED();
|
||||
Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
|
||||
R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
|
||||
AppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);
|
||||
R_RETURN(m_container->CreateManagedLayer(out_layer_id, display_id, aruid.pid));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::DestroyManagedLayer(u64 layer_id) {
|
||||
LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
|
||||
R_RETURN(m_container->DestroyManagedLayer(layer_id));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) {
|
||||
@ -124,8 +133,8 @@ Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) {
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id={}, visible={}", layer_id, visible);
|
||||
R_SUCCEED();
|
||||
LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
|
||||
R_RETURN(m_container->SetLayerVisibility(layer_id, visible));
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -4,22 +4,34 @@
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class Container;
|
||||
|
||||
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
|
||||
public:
|
||||
explicit IManagerDisplayService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::Nvnflinger> surface_flinger);
|
||||
explicit IManagerDisplayService(Core::System& system_, std::shared_ptr<Container> container);
|
||||
~IManagerDisplayService() override;
|
||||
|
||||
private:
|
||||
Result CreateManagedLayer(Out<u64> out_layer_id, u32 unknown, u64 display_id,
|
||||
Result CreateSharedLayerSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
|
||||
u64* out_layer_handle, u64 display_id, bool enable_blending);
|
||||
void DestroySharedLayerSession(Kernel::KProcess* owner_process);
|
||||
|
||||
Result SetLayerBlending(bool enabled, u64 layer_id);
|
||||
|
||||
public:
|
||||
Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
|
||||
AppletResourceUserId aruid);
|
||||
Result DestroyManagedLayer(u64 layer_id);
|
||||
Result AddToLayerStack(u32 stack_id, u64 layer_id);
|
||||
Result SetLayerVisibility(bool visible, u64 layer_id);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Nvnflinger::Nvnflinger> m_surface_flinger;
|
||||
const std::shared_ptr<Container> m_container;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/manager_root_service.h"
|
||||
#include "core/hle/service/vi/service_creator.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
@ -11,11 +12,9 @@
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
IManagerRootService::IManagerRootService(
|
||||
Core::System& system_, std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager)
|
||||
: ServiceFramework{system_, "vi:m"}, m_binder_service{std::move(binder_service)},
|
||||
m_shared_buffer_manager{std::move(shared_buffer_manager)} {
|
||||
IManagerRootService::IManagerRootService(Core::System& system_,
|
||||
std::shared_ptr<Container> container)
|
||||
: ServiceFramework{system_, "vi:m"}, m_container{std::move(container)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{2, C<&IManagerRootService::GetDisplayService>, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
@ -32,8 +31,8 @@ IManagerRootService::~IManagerRootService() = default;
|
||||
Result IManagerRootService::GetDisplayService(
|
||||
Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_binder_service,
|
||||
m_shared_buffer_manager, Permission::Manager, policy));
|
||||
R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
|
||||
Permission::Manager, policy));
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -10,21 +10,15 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class IHOSBinderDriver;
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class FbshareBufferManager;
|
||||
class Container;
|
||||
class IApplicationDisplayService;
|
||||
enum class Policy : u32;
|
||||
|
||||
class IManagerRootService final : public ServiceFramework<IManagerRootService> {
|
||||
public:
|
||||
explicit IManagerRootService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager);
|
||||
explicit IManagerRootService(Core::System& system_, std::shared_ptr<Container> container);
|
||||
~IManagerRootService() override;
|
||||
|
||||
Result GetDisplayService(
|
||||
@ -32,8 +26,7 @@ public:
|
||||
Policy policy);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_service;
|
||||
const std::shared_ptr<FbshareBufferManager> m_shared_buffer_manager;
|
||||
const std::shared_ptr<Container> m_container;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -22,8 +22,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
|
||||
|
||||
Result GetApplicationDisplayService(
|
||||
std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
|
||||
Core::System& system, std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager, Permission permission,
|
||||
Core::System& system, std::shared_ptr<Container> container, Permission permission,
|
||||
Policy policy) {
|
||||
|
||||
if (!IsValidServiceAccess(permission, policy)) {
|
||||
@ -32,7 +31,7 @@ Result GetApplicationDisplayService(
|
||||
}
|
||||
|
||||
*out_application_display_service =
|
||||
std::make_shared<IApplicationDisplayService>(system, binder_service, shared_buffer_manager);
|
||||
std::make_shared<IApplicationDisplayService>(system, std::move(container));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -11,23 +11,18 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class IHOSBinderDriver;
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
union Result;
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class FbshareBufferManager;
|
||||
class Container;
|
||||
class IApplicationDisplayService;
|
||||
enum class Permission;
|
||||
enum class Policy : u32;
|
||||
|
||||
Result GetApplicationDisplayService(
|
||||
std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
|
||||
Core::System& system, std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager, Permission permission,
|
||||
Core::System& system, std::shared_ptr<Container> container, Permission permission,
|
||||
Policy policy);
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -11,8 +11,8 @@
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
||||
#include "core/hle/service/nvnflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||
#include "core/hle/service/vi/fbshare_buffer_manager.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
@ -203,16 +203,15 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
|
||||
|
||||
} // namespace
|
||||
|
||||
FbshareBufferManager::FbshareBufferManager(Core::System& system,
|
||||
std::shared_ptr<Nvnflinger::Nvnflinger> surface_flinger,
|
||||
std::shared_ptr<Nvidia::Module> nvdrv)
|
||||
: m_system(system), m_surface_flinger(std::move(surface_flinger)), m_nvdrv(std::move(nvdrv)) {}
|
||||
SharedBufferManager::SharedBufferManager(Core::System& system, Container& container,
|
||||
std::shared_ptr<Nvidia::Module> nvdrv)
|
||||
: m_system(system), m_container(container), m_nvdrv(std::move(nvdrv)) {}
|
||||
|
||||
FbshareBufferManager::~FbshareBufferManager() = default;
|
||||
SharedBufferManager::~SharedBufferManager() = default;
|
||||
|
||||
Result FbshareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id,
|
||||
u64* out_layer_handle, u64 display_id,
|
||||
Nvnflinger::LayerBlending blending) {
|
||||
Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
|
||||
u64* out_layer_handle, u64 display_id,
|
||||
bool enable_blending) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Ensure we haven't already created.
|
||||
@ -237,7 +236,7 @@ Result FbshareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou
|
||||
owner_process, m_system));
|
||||
|
||||
// Create new session.
|
||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, FbshareSession{});
|
||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
||||
auto& session = it->second;
|
||||
|
||||
auto& container = m_nvdrv->GetContainer();
|
||||
@ -249,17 +248,18 @@ Result FbshareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou
|
||||
session.nvmap_fd, map_address, SharedBufferSize));
|
||||
|
||||
// Create and open a layer for the display.
|
||||
session.layer_id = m_surface_flinger->CreateLayer(m_display_id, blending).value();
|
||||
m_surface_flinger->OpenLayer(session.layer_id);
|
||||
s32 producer_binder_id;
|
||||
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
|
||||
std::addressof(session.layer_id), display_id));
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer = m_surface_flinger->FindLayer(m_display_id, session.layer_id);
|
||||
ASSERT(layer != nullptr);
|
||||
// Configure blending.
|
||||
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
|
||||
|
||||
// Get the producer and set preallocated buffers.
|
||||
auto& producer = layer->GetBufferQueue();
|
||||
MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle);
|
||||
MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle);
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
|
||||
MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle);
|
||||
MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle);
|
||||
|
||||
// Assign outputs.
|
||||
*out_buffer_id = m_buffer_id;
|
||||
@ -269,7 +269,7 @@ Result FbshareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FbshareBufferManager::Finalize(Kernel::KProcess* owner_process) {
|
||||
void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
if (m_buffer_id == 0) {
|
||||
@ -285,7 +285,7 @@ void FbshareBufferManager::Finalize(Kernel::KProcess* owner_process) {
|
||||
auto& session = it->second;
|
||||
|
||||
// Destroy the layer.
|
||||
m_surface_flinger->DestroyLayer(session.layer_id);
|
||||
R_ASSERT(m_container.DestroyStrayLayer(session.layer_id));
|
||||
|
||||
// Close nvmap handle.
|
||||
FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
|
||||
@ -301,11 +301,11 @@ void FbshareBufferManager::Finalize(Kernel::KProcess* owner_process) {
|
||||
m_sessions.erase(it);
|
||||
}
|
||||
|
||||
Result FbshareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||
s32* out_nvmap_handle,
|
||||
SharedMemoryPoolLayout* out_pool_layout,
|
||||
u64 buffer_id,
|
||||
u64 applet_resource_user_id) {
|
||||
Result SharedBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||
s32* out_nvmap_handle,
|
||||
SharedMemoryPoolLayout* out_pool_layout,
|
||||
u64 buffer_id,
|
||||
u64 applet_resource_user_id) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
|
||||
@ -319,36 +319,20 @@ Result FbshareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbshareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
|
||||
// Ensure the layer id is valid.
|
||||
R_UNLESS(layer_id > 0, VI::ResultNotFound);
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer = m_surface_flinger->FindLayer(m_display_id, layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
// We succeeded.
|
||||
*out_layer = layer;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbshareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
|
||||
std::array<s32, 4>& out_slot_indexes,
|
||||
s64* out_target_slot, u64 layer_id) {
|
||||
Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
|
||||
std::array<s32, 4>& out_slot_indexes,
|
||||
s64* out_target_slot, u64 layer_id) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer;
|
||||
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
|
||||
|
||||
// Get the producer.
|
||||
auto& producer = layer->GetBufferQueue();
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
|
||||
|
||||
// Get the next buffer from the producer.
|
||||
s32 slot;
|
||||
R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
|
||||
SharedBufferWidth, SharedBufferHeight,
|
||||
SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
|
||||
R_UNLESS(producer->DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
|
||||
SharedBufferWidth, SharedBufferHeight,
|
||||
SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
// Assign remaining outputs.
|
||||
@ -359,27 +343,24 @@ Result FbshareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbshareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
Common::Rectangle<s32> crop_region,
|
||||
u32 transform, s32 swap_interval,
|
||||
u64 layer_id, s64 slot) {
|
||||
Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
Common::Rectangle<s32> crop_region,
|
||||
u32 transform, s32 swap_interval, u64 layer_id,
|
||||
s64 slot) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer;
|
||||
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
|
||||
|
||||
// Get the producer.
|
||||
auto& producer = layer->GetBufferQueue();
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
|
||||
|
||||
// Request to queue the buffer.
|
||||
std::shared_ptr<android::GraphicBuffer> buffer;
|
||||
R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
|
||||
R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
ON_RESULT_FAILURE {
|
||||
producer.CancelBuffer(static_cast<s32>(slot), fence);
|
||||
producer->CancelBuffer(static_cast<s32>(slot), fence);
|
||||
};
|
||||
|
||||
// Queue the buffer to the producer.
|
||||
@ -389,7 +370,7 @@ Result FbshareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
input.fence = fence;
|
||||
input.transform = static_cast<android::NativeWindowTransform>(transform);
|
||||
input.swap_interval = swap_interval;
|
||||
R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
|
||||
R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
@ -397,25 +378,36 @@ Result FbshareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbshareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
|
||||
u64 layer_id) {
|
||||
Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer;
|
||||
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
|
||||
|
||||
// Get the producer.
|
||||
auto& producer = layer->GetBufferQueue();
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
|
||||
|
||||
// Set the event.
|
||||
*out_event = std::addressof(producer.GetNativeHandle());
|
||||
// Cancel.
|
||||
producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence());
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbshareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
|
||||
Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
|
||||
u64 layer_id) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Get the producer.
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
|
||||
|
||||
// Set the event.
|
||||
*out_event = producer->GetNativeHandle({});
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
|
||||
std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
|
||||
Common::ScratchBuffer<u32> scratch;
|
||||
|
@ -8,16 +8,28 @@
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KPageGroup;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::android {
|
||||
class BufferQueueProducer;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class Module;
|
||||
}
|
||||
|
||||
union Result;
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class Container;
|
||||
|
||||
struct SharedMemorySlot {
|
||||
u64 buffer_offset;
|
||||
u64 size;
|
||||
@ -32,18 +44,17 @@ struct SharedMemoryPoolLayout {
|
||||
};
|
||||
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
|
||||
|
||||
struct FbshareSession;
|
||||
struct SharedBufferSession;
|
||||
|
||||
class FbshareBufferManager final {
|
||||
class SharedBufferManager final {
|
||||
public:
|
||||
explicit FbshareBufferManager(Core::System& system,
|
||||
std::shared_ptr<Nvnflinger::Nvnflinger> surface_flinger,
|
||||
std::shared_ptr<Nvidia::Module> nvdrv);
|
||||
~FbshareBufferManager();
|
||||
explicit SharedBufferManager(Core::System& system, Container& container,
|
||||
std::shared_ptr<Nvidia::Module> nvdrv);
|
||||
~SharedBufferManager();
|
||||
|
||||
Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
|
||||
u64 display_id, Nvnflinger::LayerBlending blending);
|
||||
void Finalize(Kernel::KProcess* owner_process);
|
||||
Result CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
|
||||
u64 display_id, bool enable_blending);
|
||||
void DestroySession(Kernel::KProcess* owner_process);
|
||||
|
||||
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
|
||||
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
|
||||
@ -52,28 +63,26 @@ public:
|
||||
s64* out_target_slot, u64 layer_id);
|
||||
Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
|
||||
u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
|
||||
Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
|
||||
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
|
||||
|
||||
private:
|
||||
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
|
||||
|
||||
private:
|
||||
u64 m_next_buffer_id = 1;
|
||||
u64 m_display_id = 0;
|
||||
u64 m_buffer_id = 0;
|
||||
SharedMemoryPoolLayout m_pool_layout = {};
|
||||
std::map<u64, FbshareSession> m_sessions;
|
||||
std::map<u64, SharedBufferSession> m_sessions;
|
||||
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
|
||||
|
||||
std::mutex m_guard;
|
||||
Core::System& m_system;
|
||||
const std::shared_ptr<Nvnflinger::Nvnflinger> m_surface_flinger;
|
||||
Container& m_container;
|
||||
const std::shared_ptr<Nvidia::Module> m_nvdrv;
|
||||
};
|
||||
|
||||
struct FbshareSession {
|
||||
struct SharedBufferSession {
|
||||
Nvidia::DeviceFD nvmap_fd = {};
|
||||
Nvidia::NvCore::SessionId session_id = {};
|
||||
u64 layer_id = {};
|
@ -3,17 +3,15 @@
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/system_display_service.h"
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
ISystemDisplayService::ISystemDisplayService(
|
||||
Core::System& system_, std::shared_ptr<Nvnflinger::Nvnflinger> surface_flinger,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager)
|
||||
: ServiceFramework{system_, "ISystemDisplayService"},
|
||||
m_surface_flinger{std::move(surface_flinger)},
|
||||
m_shared_buffer_manager{std::move(shared_buffer_manager)} {
|
||||
ISystemDisplayService::ISystemDisplayService(Core::System& system_,
|
||||
std::shared_ptr<Container> container)
|
||||
: ServiceFramework{system_, "ISystemDisplayService"}, m_container{std::move(container)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1200, nullptr, "GetZOrderCountMin"},
|
||||
@ -61,7 +59,7 @@ ISystemDisplayService::ISystemDisplayService(
|
||||
{8255, C<&ISystemDisplayService::PresentSharedFrameBuffer>, "PresentSharedFrameBuffer"},
|
||||
{8256, C<&ISystemDisplayService::GetSharedFrameBufferAcquirableEvent>, "GetSharedFrameBufferAcquirableEvent"},
|
||||
{8257, nullptr, "FillSharedFrameBufferColor"},
|
||||
{8258, nullptr, "CancelSharedFrameBuffer"},
|
||||
{8258, C<&ISystemDisplayService::CancelSharedFrameBuffer>, "CancelSharedFrameBuffer"},
|
||||
{9000, nullptr, "GetDp2hdmiController"},
|
||||
};
|
||||
// clang-format on
|
||||
@ -106,7 +104,7 @@ Result ISystemDisplayService::GetSharedBufferMemoryHandleId(
|
||||
ClientAppletResourceUserId aruid) {
|
||||
LOG_INFO(Service_VI, "called. buffer_id={}, aruid={:#x}", buffer_id, aruid.pid);
|
||||
|
||||
R_RETURN(m_shared_buffer_manager->GetSharedBufferMemoryHandleId(
|
||||
R_RETURN(m_container->GetSharedBufferManager()->GetSharedBufferMemoryHandleId(
|
||||
out_size, out_nvmap_handle, out_pool_layout, buffer_id, aruid.pid));
|
||||
}
|
||||
|
||||
@ -124,8 +122,8 @@ Result ISystemDisplayService::AcquireSharedFrameBuffer(Out<android::Fence> out_f
|
||||
Out<std::array<s32, 4>> out_slots,
|
||||
Out<s64> out_target_slot, u64 layer_id) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(m_shared_buffer_manager->AcquireSharedFrameBuffer(out_fence, *out_slots,
|
||||
out_target_slot, layer_id));
|
||||
R_RETURN(m_container->GetSharedBufferManager()->AcquireSharedFrameBuffer(
|
||||
out_fence, *out_slots, out_target_slot, layer_id));
|
||||
}
|
||||
|
||||
Result ISystemDisplayService::PresentSharedFrameBuffer(android::Fence fence,
|
||||
@ -133,14 +131,20 @@ Result ISystemDisplayService::PresentSharedFrameBuffer(android::Fence fence,
|
||||
u32 window_transform, s32 swap_interval,
|
||||
u64 layer_id, s64 surface_id) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(m_shared_buffer_manager->PresentSharedFrameBuffer(
|
||||
R_RETURN(m_container->GetSharedBufferManager()->PresentSharedFrameBuffer(
|
||||
fence, crop_region, window_transform, swap_interval, layer_id, surface_id));
|
||||
}
|
||||
|
||||
Result ISystemDisplayService::GetSharedFrameBufferAcquirableEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event, u64 layer_id) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(m_shared_buffer_manager->GetSharedFrameBufferAcquirableEvent(out_event, layer_id));
|
||||
R_RETURN(m_container->GetSharedBufferManager()->GetSharedFrameBufferAcquirableEvent(out_event,
|
||||
layer_id));
|
||||
}
|
||||
|
||||
Result ISystemDisplayService::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(m_container->GetSharedBufferManager()->CancelSharedFrameBuffer(layer_id, slot));
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -5,21 +5,15 @@
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/vi/fbshare_buffer_manager.h"
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class Nvnflinger;
|
||||
} // namespace Service::Nvnflinger
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class FbshareBufferManager;
|
||||
class Container;
|
||||
|
||||
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
|
||||
public:
|
||||
explicit ISystemDisplayService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::Nvnflinger> surface_flinger,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager);
|
||||
explicit ISystemDisplayService(Core::System& system_, std::shared_ptr<Container> container);
|
||||
~ISystemDisplayService() override;
|
||||
|
||||
private:
|
||||
@ -42,10 +36,10 @@ private:
|
||||
Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
|
||||
u32 window_transform, s32 swap_interval, u64 layer_id,
|
||||
s64 surface_id);
|
||||
Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Nvnflinger::Nvnflinger> m_surface_flinger;
|
||||
const std::shared_ptr<FbshareBufferManager> m_shared_buffer_manager;
|
||||
const std::shared_ptr<Container> m_container;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/service_creator.h"
|
||||
#include "core/hle/service/vi/system_root_service.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
@ -10,11 +11,8 @@
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
ISystemRootService::ISystemRootService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager)
|
||||
: ServiceFramework{system_, "vi:s"}, m_binder_service{std::move(binder_service)},
|
||||
m_shared_buffer_manager{std::move(shared_buffer_manager)} {
|
||||
ISystemRootService::ISystemRootService(Core::System& system_, std::shared_ptr<Container> container)
|
||||
: ServiceFramework{system_, "vi:s"}, m_container{std::move(container)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, C<&ISystemRootService::GetDisplayService>, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
@ -27,8 +25,8 @@ ISystemRootService::~ISystemRootService() = default;
|
||||
Result ISystemRootService::GetDisplayService(
|
||||
Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_binder_service,
|
||||
m_shared_buffer_manager, Permission::System, policy));
|
||||
R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
|
||||
Permission::System, policy));
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -10,21 +10,15 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class IHOSBinderDriver;
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class FbshareBufferManager;
|
||||
class Container;
|
||||
class IApplicationDisplayService;
|
||||
enum class Policy : u32;
|
||||
|
||||
class ISystemRootService final : public ServiceFramework<ISystemRootService> {
|
||||
public:
|
||||
explicit ISystemRootService(Core::System& system_,
|
||||
std::shared_ptr<Nvnflinger::IHOSBinderDriver> binder_service,
|
||||
std::shared_ptr<FbshareBufferManager> shared_buffer_manager);
|
||||
explicit ISystemRootService(Core::System& system_, std::shared_ptr<Container> container);
|
||||
~ISystemRootService() override;
|
||||
|
||||
private:
|
||||
@ -32,8 +26,7 @@ private:
|
||||
Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
|
||||
Policy policy);
|
||||
|
||||
const std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_service;
|
||||
const std::shared_ptr<FbshareBufferManager> m_shared_buffer_manager;
|
||||
const std::shared_ptr<Container> m_container;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -2,38 +2,29 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv_interface.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/application_root_service.h"
|
||||
#include "core/hle/service/vi/fbshare_buffer_manager.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/manager_root_service.h"
|
||||
#include "core/hle/service/vi/system_root_service.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
const auto binder_service =
|
||||
system.ServiceManager().GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true);
|
||||
const auto nvdrv =
|
||||
system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
|
||||
const auto shared_buffer_manager =
|
||||
std::make_shared<FbshareBufferManager>(system, binder_service->GetSurfaceFlinger(), nvdrv);
|
||||
void LoopProcess(Core::System& system, std::stop_token token) {
|
||||
const auto container = std::make_shared<Container>(system);
|
||||
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("vi:m",
|
||||
std::make_shared<IManagerRootService>(system, container));
|
||||
server_manager->RegisterNamedService("vi:s",
|
||||
std::make_shared<ISystemRootService>(system, container));
|
||||
server_manager->RegisterNamedService(
|
||||
"vi:m",
|
||||
std::make_shared<IManagerRootService>(system, binder_service, shared_buffer_manager));
|
||||
server_manager->RegisterNamedService(
|
||||
"vi:s",
|
||||
std::make_shared<ISystemRootService>(system, binder_service, shared_buffer_manager));
|
||||
server_manager->RegisterNamedService(
|
||||
"vi:u",
|
||||
std::make_shared<IApplicationRootService>(system, binder_service, shared_buffer_manager));
|
||||
"vi:u", std::make_shared<IApplicationRootService>(system, container));
|
||||
|
||||
std::stop_callback cb(token, [=] { container->OnTerminate(); });
|
||||
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
void LoopProcess(Core::System& system, std::stop_token token);
|
||||
|
||||
} // namespace Service::VI
|
||||
|
@ -68,7 +68,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
|
||||
|
||||
class NativeWindow final {
|
||||
public:
|
||||
constexpr explicit NativeWindow(u32 id_) : id{id_} {}
|
||||
constexpr explicit NativeWindow(s32 id_) : id{static_cast<u64>(id_)} {}
|
||||
constexpr explicit NativeWindow(const NativeWindow& other) = default;
|
||||
|
||||
private:
|
||||
|
26
src/core/hle/service/vi/vsync_manager.cpp
Normal file
26
src/core/hle/service/vi/vsync_manager.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/vi/vsync_manager.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VsyncManager::VsyncManager() = default;
|
||||
VsyncManager::~VsyncManager() = default;
|
||||
|
||||
void VsyncManager::SignalVsync() {
|
||||
for (auto* event : m_vsync_events) {
|
||||
event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void VsyncManager::LinkVsyncEvent(Event* event) {
|
||||
m_vsync_events.insert(event);
|
||||
}
|
||||
|
||||
void VsyncManager::UnlinkVsyncEvent(Event* event) {
|
||||
m_vsync_events.erase(event);
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
29
src/core/hle/service/vi/vsync_manager.h
Normal file
29
src/core/hle/service/vi/vsync_manager.h
Normal file
@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace Service {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class DisplayList;
|
||||
|
||||
class VsyncManager {
|
||||
public:
|
||||
explicit VsyncManager();
|
||||
~VsyncManager();
|
||||
|
||||
void SignalVsync();
|
||||
void LinkVsyncEvent(Event* event);
|
||||
void UnlinkVsyncEvent(Event* event);
|
||||
|
||||
private:
|
||||
std::set<Event*> m_vsync_events;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
Loading…
Reference in New Issue
Block a user