// 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("dispdrv", true); m_surface_flinger = m_binder_driver->GetSurfaceFlinger(); const auto nvdrv = system.ServiceManager().GetService("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* out_binder_driver) { *out_binder_driver = m_binder_driver; R_SUCCEED(); } Result Container::GetLayerProducerHandle( std::shared_ptr* 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(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()); } bool Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id) { std::scoped_lock lk{m_lock}; return m_surface_flinger->ComposeDisplay(out_swap_interval, out_compose_speed_scale, display_id); } } // namespace Service::VI