mirror of https://git.suyu.dev/suyu/suyu
vi: manage resources independently of nvnflinger and refactor
parent
dcce9837d2
commit
812f23d05c
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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