From 809148e1a58296ab88c9d3c6719d345f35ce0279 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 19 Feb 2023 15:05:34 -0500 Subject: nvnflinger: fix name --- src/common/logging/filter.cpp | 2 +- src/common/logging/types.h | 202 ++--- src/core/CMakeLists.txt | 58 +- src/core/hle/service/am/am.cpp | 23 +- src/core/hle/service/am/am.h | 10 +- src/core/hle/service/am/applet_ae.cpp | 36 +- src/core/hle/service/am/applet_ae.h | 8 +- src/core/hle/service/am/applet_oe.cpp | 22 +- src/core/hle/service/am/applet_oe.h | 8 +- src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | 4 +- src/core/hle/service/nvdrv/nvdrv.cpp | 6 +- src/core/hle/service/nvdrv/nvdrv.h | 10 +- src/core/hle/service/nvflinger/binder.h | 45 - src/core/hle/service/nvflinger/buffer_item.h | 46 - .../hle/service/nvflinger/buffer_item_consumer.cpp | 59 -- .../hle/service/nvflinger/buffer_item_consumer.h | 28 - .../service/nvflinger/buffer_queue_consumer.cpp | 213 ----- .../hle/service/nvflinger/buffer_queue_consumer.h | 43 - .../hle/service/nvflinger/buffer_queue_core.cpp | 115 --- src/core/hle/service/nvflinger/buffer_queue_core.h | 80 -- src/core/hle/service/nvflinger/buffer_queue_defs.h | 21 - .../service/nvflinger/buffer_queue_producer.cpp | 933 --------------------- .../hle/service/nvflinger/buffer_queue_producer.h | 90 -- src/core/hle/service/nvflinger/buffer_slot.h | 38 - .../hle/service/nvflinger/buffer_transform_flags.h | 25 - src/core/hle/service/nvflinger/consumer_base.cpp | 133 --- src/core/hle/service/nvflinger/consumer_base.h | 60 -- src/core/hle/service/nvflinger/consumer_listener.h | 26 - .../service/nvflinger/graphic_buffer_producer.cpp | 18 - .../service/nvflinger/graphic_buffer_producer.h | 76 -- .../service/nvflinger/hos_binder_driver_server.cpp | 36 - .../service/nvflinger/hos_binder_driver_server.h | 37 - src/core/hle/service/nvflinger/nvflinger.cpp | 335 -------- src/core/hle/service/nvflinger/nvflinger.h | 155 ---- src/core/hle/service/nvflinger/parcel.h | 177 ---- src/core/hle/service/nvflinger/pixel_format.h | 21 - src/core/hle/service/nvflinger/producer_listener.h | 17 - src/core/hle/service/nvflinger/status.h | 28 - src/core/hle/service/nvflinger/ui/fence.h | 32 - src/core/hle/service/nvflinger/ui/graphic_buffer.h | 100 --- src/core/hle/service/nvflinger/window.h | 53 -- src/core/hle/service/nvnflinger/binder.h | 45 + src/core/hle/service/nvnflinger/buffer_item.h | 46 + .../service/nvnflinger/buffer_item_consumer.cpp | 59 ++ .../hle/service/nvnflinger/buffer_item_consumer.h | 28 + .../service/nvnflinger/buffer_queue_consumer.cpp | 213 +++++ .../hle/service/nvnflinger/buffer_queue_consumer.h | 43 + .../hle/service/nvnflinger/buffer_queue_core.cpp | 115 +++ .../hle/service/nvnflinger/buffer_queue_core.h | 80 ++ .../hle/service/nvnflinger/buffer_queue_defs.h | 21 + .../service/nvnflinger/buffer_queue_producer.cpp | 933 +++++++++++++++++++++ .../hle/service/nvnflinger/buffer_queue_producer.h | 90 ++ src/core/hle/service/nvnflinger/buffer_slot.h | 38 + .../service/nvnflinger/buffer_transform_flags.h | 25 + src/core/hle/service/nvnflinger/consumer_base.cpp | 133 +++ src/core/hle/service/nvnflinger/consumer_base.h | 60 ++ .../hle/service/nvnflinger/consumer_listener.h | 26 + .../service/nvnflinger/graphic_buffer_producer.cpp | 18 + .../service/nvnflinger/graphic_buffer_producer.h | 76 ++ .../nvnflinger/hos_binder_driver_server.cpp | 36 + .../service/nvnflinger/hos_binder_driver_server.h | 37 + src/core/hle/service/nvnflinger/nvnflinger.cpp | 335 ++++++++ src/core/hle/service/nvnflinger/nvnflinger.h | 155 ++++ src/core/hle/service/nvnflinger/parcel.h | 177 ++++ src/core/hle/service/nvnflinger/pixel_format.h | 21 + .../hle/service/nvnflinger/producer_listener.h | 17 + src/core/hle/service/nvnflinger/status.h | 28 + src/core/hle/service/nvnflinger/ui/fence.h | 32 + .../hle/service/nvnflinger/ui/graphic_buffer.h | 100 +++ src/core/hle/service/nvnflinger/window.h | 53 ++ src/core/hle/service/service.cpp | 10 +- src/core/hle/service/service.h | 10 +- src/core/hle/service/vi/display/vi_display.cpp | 12 +- src/core/hle/service/vi/display/vi_display.h | 8 +- src/core/hle/service/vi/vi.cpp | 34 +- src/core/hle/service/vi/vi.h | 14 +- src/core/hle/service/vi/vi_m.cpp | 4 +- src/core/hle/service/vi/vi_m.h | 14 +- src/core/hle/service/vi/vi_s.cpp | 4 +- src/core/hle/service/vi/vi_s.h | 14 +- src/core/hle/service/vi/vi_u.cpp | 4 +- src/core/hle/service/vi/vi_u.h | 14 +- src/video_core/framebuffer_config.h | 4 +- 83 files changed, 3307 insertions(+), 3308 deletions(-) delete mode 100644 src/core/hle/service/nvflinger/binder.h delete mode 100644 src/core/hle/service/nvflinger/buffer_item.h delete mode 100644 src/core/hle/service/nvflinger/buffer_item_consumer.cpp delete mode 100644 src/core/hle/service/nvflinger/buffer_item_consumer.h delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_consumer.cpp delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_consumer.h delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_core.cpp delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_core.h delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_defs.h delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_producer.cpp delete mode 100644 src/core/hle/service/nvflinger/buffer_queue_producer.h delete mode 100644 src/core/hle/service/nvflinger/buffer_slot.h delete mode 100644 src/core/hle/service/nvflinger/buffer_transform_flags.h delete mode 100644 src/core/hle/service/nvflinger/consumer_base.cpp delete mode 100644 src/core/hle/service/nvflinger/consumer_base.h delete mode 100644 src/core/hle/service/nvflinger/consumer_listener.h delete mode 100644 src/core/hle/service/nvflinger/graphic_buffer_producer.cpp delete mode 100644 src/core/hle/service/nvflinger/graphic_buffer_producer.h delete mode 100644 src/core/hle/service/nvflinger/hos_binder_driver_server.cpp delete mode 100644 src/core/hle/service/nvflinger/hos_binder_driver_server.h delete mode 100644 src/core/hle/service/nvflinger/nvflinger.cpp delete mode 100644 src/core/hle/service/nvflinger/nvflinger.h delete mode 100644 src/core/hle/service/nvflinger/parcel.h delete mode 100644 src/core/hle/service/nvflinger/pixel_format.h delete mode 100644 src/core/hle/service/nvflinger/producer_listener.h delete mode 100644 src/core/hle/service/nvflinger/status.h delete mode 100644 src/core/hle/service/nvflinger/ui/fence.h delete mode 100644 src/core/hle/service/nvflinger/ui/graphic_buffer.h delete mode 100644 src/core/hle/service/nvflinger/window.h create mode 100644 src/core/hle/service/nvnflinger/binder.h create mode 100644 src/core/hle/service/nvnflinger/buffer_item.h create mode 100644 src/core/hle/service/nvnflinger/buffer_item_consumer.cpp create mode 100644 src/core/hle/service/nvnflinger/buffer_item_consumer.h create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_consumer.h create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_core.cpp create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_core.h create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_defs.h create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_producer.cpp create mode 100644 src/core/hle/service/nvnflinger/buffer_queue_producer.h create mode 100644 src/core/hle/service/nvnflinger/buffer_slot.h create mode 100644 src/core/hle/service/nvnflinger/buffer_transform_flags.h create mode 100644 src/core/hle/service/nvnflinger/consumer_base.cpp create mode 100644 src/core/hle/service/nvnflinger/consumer_base.h create mode 100644 src/core/hle/service/nvnflinger/consumer_listener.h create mode 100644 src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp create mode 100644 src/core/hle/service/nvnflinger/graphic_buffer_producer.h create mode 100644 src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp create mode 100644 src/core/hle/service/nvnflinger/hos_binder_driver_server.h create mode 100644 src/core/hle/service/nvnflinger/nvnflinger.cpp create mode 100644 src/core/hle/service/nvnflinger/nvnflinger.h create mode 100644 src/core/hle/service/nvnflinger/parcel.h create mode 100644 src/core/hle/service/nvnflinger/pixel_format.h create mode 100644 src/core/hle/service/nvnflinger/producer_listener.h create mode 100644 src/core/hle/service/nvnflinger/status.h create mode 100644 src/core/hle/service/nvnflinger/ui/fence.h create mode 100644 src/core/hle/service/nvnflinger/ui/graphic_buffer.h create mode 100644 src/core/hle/service/nvnflinger/window.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index a959acb74..c95909561 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -119,7 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, NPNS) \ SUB(Service, NS) \ SUB(Service, NVDRV) \ - SUB(Service, NVFlinger) \ + SUB(Service, Nvnflinger) \ SUB(Service, OLSC) \ SUB(Service, PCIE) \ SUB(Service, PCTL) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 595c15ada..8356e3183 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -29,107 +29,107 @@ enum class Level : u8 { * filter.cpp. */ enum class Class : u8 { - Log, ///< Messages about the log system itself - Common, ///< Library routines - Common_Filesystem, ///< Filesystem interface library - Common_Memory, ///< Memory mapping and management functions - Core, ///< LLE emulation core - Core_ARM, ///< ARM CPU core - Core_Timing, ///< CoreTiming functions - Config, ///< Emulator configuration (including commandline) - Debug, ///< Debugging tools - Debug_Emulated, ///< Debug messages from the emulated programs - Debug_GPU, ///< GPU debugging tools - Debug_Breakpoint, ///< Logging breakpoints and watchpoints - Debug_GDBStub, ///< GDB Stub - Kernel, ///< The HLE implementation of the CTR kernel - Kernel_SVC, ///< Kernel system calls - Service, ///< HLE implementation of system services. Each major service - ///< should have its own subclass. - Service_ACC, ///< The ACC (Accounts) service - Service_AM, ///< The AM (Applet manager) service - Service_AOC, ///< The AOC (AddOn Content) service - Service_APM, ///< The APM (Performance) service - Service_ARP, ///< The ARP service - Service_Audio, ///< The Audio (Audio control) service - Service_BCAT, ///< The BCAT service - Service_BGTC, ///< The BGTC (Background Task Controller) service - Service_BPC, ///< The BPC service - Service_BTDRV, ///< The Bluetooth driver service - Service_BTM, ///< The BTM service - Service_Capture, ///< The capture service - Service_ERPT, ///< The error reporting service - Service_ETicket, ///< The ETicket service - Service_EUPLD, ///< The error upload service - Service_Fatal, ///< The Fatal service - Service_FGM, ///< The FGM service - Service_Friend, ///< The friend service - Service_FS, ///< The FS (Filesystem) service - Service_GRC, ///< The game recording service - Service_HID, ///< The HID (Human interface device) service - Service_IRS, ///< The IRS service - Service_JIT, ///< The JIT service - Service_LBL, ///< The LBL (LCD backlight) service - Service_LDN, ///< The LDN (Local domain network) service - Service_LDR, ///< The loader service - Service_LM, ///< The LM (Logger) service - Service_Migration, ///< The migration service - Service_Mii, ///< The Mii service - Service_MM, ///< The MM (Multimedia) service - Service_MNPP, ///< The MNPP service - Service_NCM, ///< The NCM service - Service_NFC, ///< The NFC (Near-field communication) service - Service_NFP, ///< The NFP service - Service_NGCT, ///< The NGCT (No Good Content for Terra) service - Service_NIFM, ///< The NIFM (Network interface) service - Service_NIM, ///< The NIM service - Service_NOTIF, ///< The NOTIF (Notification) service - Service_NPNS, ///< The NPNS service - Service_NS, ///< The NS services - Service_NVDRV, ///< The NVDRV (Nvidia driver) service - Service_NVFlinger, ///< The NVFlinger service - Service_OLSC, ///< The OLSC service - Service_PCIE, ///< The PCIe service - Service_PCTL, ///< The PCTL (Parental control) service - Service_PCV, ///< The PCV service - Service_PM, ///< The PM service - Service_PREPO, ///< The PREPO (Play report) service - Service_PSC, ///< The PSC service - Service_PTM, ///< The PTM service - Service_SET, ///< The SET (Settings) service - Service_SM, ///< The SM (Service manager) service - Service_SPL, ///< The SPL service - Service_SSL, ///< The SSL service - Service_TCAP, ///< The TCAP service. - Service_Time, ///< The time service - Service_USB, ///< The USB (Universal Serial Bus) service - Service_VI, ///< The VI (Video interface) service - Service_WLAN, ///< The WLAN (Wireless local area network) service - HW, ///< Low-level hardware emulation - HW_Memory, ///< Memory-map and address translation - HW_LCD, ///< LCD register emulation - HW_GPU, ///< GPU control emulation - HW_AES, ///< AES engine emulation - IPC, ///< IPC interface - Frontend, ///< Emulator UI - Render, ///< Emulator video output and hardware acceleration - Render_Software, ///< Software renderer backend - Render_OpenGL, ///< OpenGL backend - Render_Vulkan, ///< Vulkan backend - Shader, ///< Shader recompiler - Shader_SPIRV, ///< Shader SPIR-V code generation - Shader_GLASM, ///< Shader GLASM code generation - Shader_GLSL, ///< Shader GLSL code generation - Audio, ///< Audio emulation - Audio_DSP, ///< The HLE implementation of the DSP - Audio_Sink, ///< Emulator audio output backend - Loader, ///< ROM loader - CheatEngine, ///< Memory manipulation and engine VM functions - Crypto, ///< Cryptographic engine/functions - Input, ///< Input emulation - Network, ///< Network emulation - WebService, ///< Interface to yuzu Web Services - Count ///< Total number of logging classes + Log, ///< Messages about the log system itself + Common, ///< Library routines + Common_Filesystem, ///< Filesystem interface library + Common_Memory, ///< Memory mapping and management functions + Core, ///< LLE emulation core + Core_ARM, ///< ARM CPU core + Core_Timing, ///< CoreTiming functions + Config, ///< Emulator configuration (including commandline) + Debug, ///< Debugging tools + Debug_Emulated, ///< Debug messages from the emulated programs + Debug_GPU, ///< GPU debugging tools + Debug_Breakpoint, ///< Logging breakpoints and watchpoints + Debug_GDBStub, ///< GDB Stub + Kernel, ///< The HLE implementation of the CTR kernel + Kernel_SVC, ///< Kernel system calls + Service, ///< HLE implementation of system services. Each major service + ///< should have its own subclass. + Service_ACC, ///< The ACC (Accounts) service + Service_AM, ///< The AM (Applet manager) service + Service_AOC, ///< The AOC (AddOn Content) service + Service_APM, ///< The APM (Performance) service + Service_ARP, ///< The ARP service + Service_Audio, ///< The Audio (Audio control) service + Service_BCAT, ///< The BCAT service + Service_BGTC, ///< The BGTC (Background Task Controller) service + Service_BPC, ///< The BPC service + Service_BTDRV, ///< The Bluetooth driver service + Service_BTM, ///< The BTM service + Service_Capture, ///< The capture service + Service_ERPT, ///< The error reporting service + Service_ETicket, ///< The ETicket service + Service_EUPLD, ///< The error upload service + Service_Fatal, ///< The Fatal service + Service_FGM, ///< The FGM service + Service_Friend, ///< The friend service + Service_FS, ///< The FS (Filesystem) service + Service_GRC, ///< The game recording service + Service_HID, ///< The HID (Human interface device) service + Service_IRS, ///< The IRS service + Service_JIT, ///< The JIT service + Service_LBL, ///< The LBL (LCD backlight) service + Service_LDN, ///< The LDN (Local domain network) service + Service_LDR, ///< The loader service + Service_LM, ///< The LM (Logger) service + Service_Migration, ///< The migration service + Service_Mii, ///< The Mii service + Service_MM, ///< The MM (Multimedia) service + Service_MNPP, ///< The MNPP service + Service_NCM, ///< The NCM service + Service_NFC, ///< The NFC (Near-field communication) service + Service_NFP, ///< The NFP service + Service_NGCT, ///< The NGCT (No Good Content for Terra) service + Service_NIFM, ///< The NIFM (Network interface) service + Service_NIM, ///< The NIM service + Service_NOTIF, ///< The NOTIF (Notification) service + Service_NPNS, ///< The NPNS service + Service_NS, ///< The NS services + Service_NVDRV, ///< The NVDRV (Nvidia driver) service + Service_Nvnflinger, ///< The Nvnflinger service + Service_OLSC, ///< The OLSC service + Service_PCIE, ///< The PCIe service + Service_PCTL, ///< The PCTL (Parental control) service + Service_PCV, ///< The PCV service + Service_PM, ///< The PM service + Service_PREPO, ///< The PREPO (Play report) service + Service_PSC, ///< The PSC service + Service_PTM, ///< The PTM service + Service_SET, ///< The SET (Settings) service + Service_SM, ///< The SM (Service manager) service + Service_SPL, ///< The SPL service + Service_SSL, ///< The SSL service + Service_TCAP, ///< The TCAP service. + Service_Time, ///< The time service + Service_USB, ///< The USB (Universal Serial Bus) service + Service_VI, ///< The VI (Video interface) service + Service_WLAN, ///< The WLAN (Wireless local area network) service + HW, ///< Low-level hardware emulation + HW_Memory, ///< Memory-map and address translation + HW_LCD, ///< LCD register emulation + HW_GPU, ///< GPU control emulation + HW_AES, ///< AES engine emulation + IPC, ///< IPC interface + Frontend, ///< Emulator UI + Render, ///< Emulator video output and hardware acceleration + Render_Software, ///< Software renderer backend + Render_OpenGL, ///< OpenGL backend + Render_Vulkan, ///< Vulkan backend + Shader, ///< Shader recompiler + Shader_SPIRV, ///< Shader SPIR-V code generation + Shader_GLASM, ///< Shader GLASM code generation + Shader_GLSL, ///< Shader GLSL code generation + Audio, ///< Audio emulation + Audio_DSP, ///< The HLE implementation of the DSP + Audio_Sink, ///< Emulator audio output backend + Loader, ///< ROM loader + CheatEngine, ///< Memory manipulation and engine VM functions + Crypto, ///< Cryptographic engine/functions + Input, ///< Input emulation + Network, ///< Network emulation + WebService, ///< Interface to yuzu Web Services + Count ///< Total number of logging classes }; } // namespace Common::Log diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 194cdd025..4a1a8bb43 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -626,35 +626,35 @@ add_library(core STATIC hle/service/nvdrv/nvdrv_interface.h hle/service/nvdrv/nvmemp.cpp hle/service/nvdrv/nvmemp.h - hle/service/nvflinger/binder.h - hle/service/nvflinger/buffer_item.h - hle/service/nvflinger/buffer_item_consumer.cpp - hle/service/nvflinger/buffer_item_consumer.h - hle/service/nvflinger/buffer_queue_consumer.cpp - hle/service/nvflinger/buffer_queue_consumer.h - hle/service/nvflinger/buffer_queue_core.cpp - hle/service/nvflinger/buffer_queue_core.h - hle/service/nvflinger/buffer_queue_defs.h - hle/service/nvflinger/buffer_queue_producer.cpp - hle/service/nvflinger/buffer_queue_producer.h - hle/service/nvflinger/buffer_slot.h - hle/service/nvflinger/buffer_transform_flags.h - hle/service/nvflinger/consumer_base.cpp - hle/service/nvflinger/consumer_base.h - hle/service/nvflinger/consumer_listener.h - hle/service/nvflinger/graphic_buffer_producer.cpp - hle/service/nvflinger/graphic_buffer_producer.h - hle/service/nvflinger/hos_binder_driver_server.cpp - hle/service/nvflinger/hos_binder_driver_server.h - hle/service/nvflinger/nvflinger.cpp - hle/service/nvflinger/nvflinger.h - hle/service/nvflinger/parcel.h - hle/service/nvflinger/pixel_format.h - hle/service/nvflinger/producer_listener.h - hle/service/nvflinger/status.h - hle/service/nvflinger/ui/fence.h - hle/service/nvflinger/ui/graphic_buffer.h - hle/service/nvflinger/window.h + hle/service/nvnflinger/binder.h + hle/service/nvnflinger/buffer_item.h + hle/service/nvnflinger/buffer_item_consumer.cpp + hle/service/nvnflinger/buffer_item_consumer.h + hle/service/nvnflinger/buffer_queue_consumer.cpp + hle/service/nvnflinger/buffer_queue_consumer.h + hle/service/nvnflinger/buffer_queue_core.cpp + hle/service/nvnflinger/buffer_queue_core.h + hle/service/nvnflinger/buffer_queue_defs.h + hle/service/nvnflinger/buffer_queue_producer.cpp + hle/service/nvnflinger/buffer_queue_producer.h + hle/service/nvnflinger/buffer_slot.h + hle/service/nvnflinger/buffer_transform_flags.h + hle/service/nvnflinger/consumer_base.cpp + hle/service/nvnflinger/consumer_base.h + hle/service/nvnflinger/consumer_listener.h + hle/service/nvnflinger/graphic_buffer_producer.cpp + hle/service/nvnflinger/graphic_buffer_producer.h + hle/service/nvnflinger/hos_binder_driver_server.cpp + hle/service/nvnflinger/hos_binder_driver_server.h + hle/service/nvnflinger/nvnflinger.cpp + hle/service/nvnflinger/nvnflinger.h + hle/service/nvnflinger/parcel.h + hle/service/nvnflinger/pixel_format.h + hle/service/nvnflinger/producer_listener.h + hle/service/nvnflinger/status.h + hle/service/nvnflinger/ui/fence.h + hle/service/nvnflinger/ui/graphic_buffer.h + hle/service/nvnflinger/window.h hle/service/olsc/olsc.cpp hle/service/olsc/olsc.h hle/service/pcie/pcie.cpp diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 00b096f9e..f74c7b550 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -30,7 +30,7 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/ns.h" -#include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm.h" @@ -251,10 +251,9 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) IDebugFunctions::~IDebugFunctions() = default; -ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_) - : ServiceFramework{system_, "ISelfController"}, nvflinger{nvflinger_}, service_context{ - system, - "ISelfController"} { +ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) + : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, + service_context{system, "ISelfController"} { // clang-format off static const FunctionInfo functions[] = { {0, &ISelfController::Exit, "Exit"}, @@ -470,8 +469,8 @@ void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) { // 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 = nvflinger.OpenDisplay("Default"); - const auto layer_id = nvflinger.CreateLayer(*display_id); + const auto display_id = nvnflinger.OpenDisplay("Default"); + const auto layer_id = nvnflinger.CreateLayer(*display_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -488,8 +487,8 @@ void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) // 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 = nvflinger.OpenDisplay("Default"); - const auto layer_id = nvflinger.CreateLayer(*display_id); + const auto display_id = nvnflinger.OpenDisplay("Default"); + const auto layer_id = nvnflinger.CreateLayer(*display_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1826,7 +1825,7 @@ void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void LoopProcess(NVFlinger::NVFlinger& nvflinger, Core::System& system) { +void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { auto message_queue = std::make_shared(system); // Needed on game boot message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); @@ -1834,9 +1833,9 @@ void LoopProcess(NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto server_manager = std::make_unique(system); server_manager->RegisterNamedService( - "appletAE", std::make_shared(nvflinger, message_queue, system)); + "appletAE", std::make_shared(nvnflinger, message_queue, system)); server_manager->RegisterNamedService( - "appletOE", std::make_shared(nvflinger, message_queue, system)); + "appletOE", std::make_shared(nvnflinger, message_queue, system)); server_manager->RegisterNamedService("idle:sys", std::make_shared(system)); server_manager->RegisterNamedService("omm", std::make_shared(system)); server_manager->RegisterNamedService("spsm", std::make_shared(system)); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index fd3d4ddef..0dbc6485e 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -16,8 +16,8 @@ class KReadableEvent; class KTransferMemory; } // namespace Kernel -namespace Service::NVFlinger { -class NVFlinger; +namespace Service::Nvnflinger { +class Nvnflinger; } namespace Service::AM { @@ -154,7 +154,7 @@ public: class ISelfController final : public ServiceFramework { public: - explicit ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_); + explicit ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_); ~ISelfController() override; private: @@ -191,7 +191,7 @@ private: Disable = 2, }; - NVFlinger::NVFlinger& nvflinger; + Nvnflinger::Nvnflinger& nvnflinger; KernelHelpers::ServiceContext service_context; @@ -397,6 +397,6 @@ public: ~IProcessWindingController() override; }; -void LoopProcess(NVFlinger::NVFlinger& nvflinger, Core::System& system); +void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index e15b5ccfa..2764f7ceb 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -6,17 +6,17 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" namespace Service::AM { class ILibraryAppletProxy final : public ServiceFramework { public: - explicit ILibraryAppletProxy(NVFlinger::NVFlinger& nvflinger_, + explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, std::shared_ptr msg_queue_, Core::System& system_) - : ServiceFramework{system_, "ILibraryAppletProxy"}, nvflinger{nvflinger_}, - msg_queue{std::move(msg_queue_)} { + : ServiceFramework{system_, "ILibraryAppletProxy"}, + nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -49,7 +49,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nvflinger); + rb.PushIpcInterface(system, nvnflinger); } void GetWindowController(HLERequestContext& ctx) { @@ -108,17 +108,17 @@ private: rb.PushIpcInterface(system); } - NVFlinger::NVFlinger& nvflinger; + Nvnflinger::Nvnflinger& nvnflinger; std::shared_ptr msg_queue; }; class ISystemAppletProxy final : public ServiceFramework { public: - explicit ISystemAppletProxy(NVFlinger::NVFlinger& nvflinger_, + explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, std::shared_ptr msg_queue_, Core::System& system_) - : ServiceFramework{system_, "ISystemAppletProxy"}, nvflinger{nvflinger_}, - msg_queue{std::move(msg_queue_)} { + : ServiceFramework{system_, "ISystemAppletProxy"}, + nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { // clang-format off static const FunctionInfo functions[] = { {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -153,7 +153,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nvflinger); + rb.PushIpcInterface(system, nvnflinger); } void GetWindowController(HLERequestContext& ctx) { @@ -220,7 +220,7 @@ private: rb.PushIpcInterface(system); } - NVFlinger::NVFlinger& nvflinger; + Nvnflinger::Nvnflinger& nvnflinger; std::shared_ptr msg_queue; }; @@ -229,7 +229,7 @@ void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(nvflinger, msg_queue, system); + rb.PushIpcInterface(nvnflinger, msg_queue, system); } void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) { @@ -237,7 +237,7 @@ void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(nvflinger, msg_queue, system); + rb.PushIpcInterface(nvnflinger, msg_queue, system); } void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) { @@ -245,13 +245,13 @@ void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(nvflinger, msg_queue, system); + rb.PushIpcInterface(nvnflinger, msg_queue, system); } -AppletAE::AppletAE(NVFlinger::NVFlinger& nvflinger_, std::shared_ptr msg_queue_, - Core::System& system_) - : ServiceFramework{system_, "appletAE"}, nvflinger{nvflinger_}, msg_queue{ - std::move(msg_queue_)} { +AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr msg_queue_, Core::System& system_) + : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_}, msg_queue{ + std::move(msg_queue_)} { // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index df8dccdc0..538ce2903 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -12,8 +12,8 @@ namespace FileSystem { class FileSystemController; } -namespace NVFlinger { -class NVFlinger; +namespace Nvnflinger { +class Nvnflinger; } namespace AM { @@ -22,7 +22,7 @@ class AppletMessageQueue; class AppletAE final : public ServiceFramework { public: - explicit AppletAE(NVFlinger::NVFlinger& nvflinger_, + explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, std::shared_ptr msg_queue_, Core::System& system_); ~AppletAE() override; @@ -33,7 +33,7 @@ private: void OpenLibraryAppletProxy(HLERequestContext& ctx); void OpenLibraryAppletProxyOld(HLERequestContext& ctx); - NVFlinger::NVFlinger& nvflinger; + Nvnflinger::Nvnflinger& nvnflinger; std::shared_ptr msg_queue; }; diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 75c330f0a..d6c565d85 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -5,17 +5,17 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" namespace Service::AM { class IApplicationProxy final : public ServiceFramework { public: - explicit IApplicationProxy(NVFlinger::NVFlinger& nvflinger_, + explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, std::shared_ptr msg_queue_, Core::System& system_) - : ServiceFramework{system_, "IApplicationProxy"}, nvflinger{nvflinger_}, - msg_queue{std::move(msg_queue_)} { + : ServiceFramework{system_, "IApplicationProxy"}, + nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { // clang-format off static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -71,7 +71,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nvflinger); + rb.PushIpcInterface(system, nvnflinger); } void GetCommonStateGetter(HLERequestContext& ctx) { @@ -98,7 +98,7 @@ private: rb.PushIpcInterface(system); } - NVFlinger::NVFlinger& nvflinger; + Nvnflinger::Nvnflinger& nvnflinger; std::shared_ptr msg_queue; }; @@ -107,13 +107,13 @@ void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(nvflinger, msg_queue, system); + rb.PushIpcInterface(nvnflinger, msg_queue, system); } -AppletOE::AppletOE(NVFlinger::NVFlinger& nvflinger_, std::shared_ptr msg_queue_, - Core::System& system_) - : ServiceFramework{system_, "appletOE"}, nvflinger{nvflinger_}, msg_queue{ - std::move(msg_queue_)} { +AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr msg_queue_, Core::System& system_) + : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_}, msg_queue{ + std::move(msg_queue_)} { static const FunctionInfo functions[] = { {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index f34e4224e..39eccc4ab 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -12,8 +12,8 @@ namespace FileSystem { class FileSystemController; } -namespace NVFlinger { -class NVFlinger; +namespace Nvnflinger { +class Nvnflinger; } namespace AM { @@ -22,7 +22,7 @@ class AppletMessageQueue; class AppletOE final : public ServiceFramework { public: - explicit AppletOE(NVFlinger::NVFlinger& nvflinger_, + explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, std::shared_ptr msg_queue_, Core::System& system_); ~AppletOE() override; @@ -31,7 +31,7 @@ public: private: void OpenApplicationProxy(HLERequestContext& ctx); - NVFlinger::NVFlinger& nvflinger; + Nvnflinger::Nvnflinger& nvnflinger; std::shared_ptr msg_queue; }; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 81bd7960a..bcd0e3ed5 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -8,8 +8,8 @@ #include "common/common_types.h" #include "common/math_util.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -#include "core/hle/service/nvflinger/buffer_transform_flags.h" -#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/buffer_transform_flags.h" +#include "core/hle/service/nvnflinger/pixel_format.h" namespace Service::Nvidia::NvCore { class Container; diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 5e71ec99f..3d774eec4 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -23,7 +23,7 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" -#include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/server_manager.h" #include "video_core/gpu.h" @@ -42,7 +42,7 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) { module.service_context.CloseEvent(event); } -void LoopProcess(NVFlinger::NVFlinger& nvflinger, Core::System& system) { +void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { auto server_manager = std::make_unique(system); auto module = std::make_shared(system); server_manager->RegisterNamedService("nvdrv", std::make_shared(system, module, "nvdrv")); @@ -53,7 +53,7 @@ void LoopProcess(NVFlinger::NVFlinger& nvflinger, Core::System& system) { server_manager->RegisterNamedService("nvdrv:t", std::make_shared(system, module, "nvdrv:t")); server_manager->RegisterNamedService("nvmemp", std::make_shared(system)); - nvflinger.SetNVDrvInstance(module); + nvnflinger.SetNVDrvInstance(module); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index b2270cf76..668be742b 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -16,7 +16,7 @@ #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/nvflinger/ui/fence.h" +#include "core/hle/service/nvnflinger/ui/fence.h" #include "core/hle/service/service.h" namespace Core { @@ -27,8 +27,8 @@ namespace Kernel { class KEvent; } -namespace Service::NVFlinger { -class NVFlinger; +namespace Service::Nvnflinger { +class Nvnflinger; } namespace Service::Nvidia { @@ -95,7 +95,7 @@ public: private: friend class EventInterface; - friend class Service::NVFlinger::NVFlinger; + friend class Service::Nvnflinger::Nvnflinger; /// Manages syncpoints on the host NvCore::Container container; @@ -114,6 +114,6 @@ private: std::unordered_map> builders; }; -void LoopProcess(NVFlinger::NVFlinger& nvflinger, Core::System& system); +void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h deleted file mode 100644 index aef1477e3..000000000 --- a/src/core/hle/service/nvflinger/binder.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h - -#pragma once - -#include "common/common_types.h" - -namespace Kernel { -class KReadableEvent; -} // namespace Kernel - -namespace Service { -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(HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0; - virtual Kernel::KReadableEvent& GetNativeHandle() = 0; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h deleted file mode 100644 index f73dec4f1..000000000 --- a/src/core/hle/service/nvflinger/buffer_item.h +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h - -#pragma once - -#include - -#include "common/common_types.h" -#include "common/math_util.h" -#include "core/hle/service/nvflinger/ui/fence.h" -#include "core/hle/service/nvflinger/window.h" - -namespace Service::android { - -class GraphicBuffer; - -class BufferItem final { -public: - constexpr BufferItem() = default; - - std::shared_ptr graphic_buffer; - Fence fence; - Common::Rectangle crop; - NativeWindowTransform transform{}; - u32 scaling_mode{}; - s64 timestamp{}; - bool is_auto_timestamp{}; - u64 frame_number{}; - - // The default value for buf, used to indicate this doesn't correspond to a slot. - static constexpr s32 INVALID_BUFFER_SLOT = -1; - union { - s32 slot{INVALID_BUFFER_SLOT}; - s32 buf; - }; - - bool is_droppable{}; - bool acquire_called{}; - bool transform_to_display_inverse{}; - s32 swap_interval{}; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp deleted file mode 100644 index 152bb5bdf..000000000 --- a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/hle/service/nvflinger/buffer_item.h" -#include "core/hle/service/nvflinger/buffer_item_consumer.h" -#include "core/hle/service/nvflinger/buffer_queue_consumer.h" - -namespace Service::android { - -BufferItemConsumer::BufferItemConsumer(std::unique_ptr consumer_) - : ConsumerBase{std::move(consumer_)} {} - -Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, - bool wait_for_fence) { - if (!item) { - return Status::BadValue; - } - - std::scoped_lock lock{mutex}; - - if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { - if (status != Status::NoBufferAvailable) { - LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status); - } - return status; - } - - if (wait_for_fence) { - UNIMPLEMENTED(); - } - - item->graphic_buffer = slots[item->slot].graphic_buffer; - - return Status::NoError; -} - -Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, const Fence& release_fence) { - std::scoped_lock lock{mutex}; - - if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); - status != Status::NoError) { - LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status); - } - - if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer); - status != Status::NoError) { - LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status); - return status; - } - - return Status::NoError; -} - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h deleted file mode 100644 index a5c655d9e..000000000 --- a/src/core/hle/service/nvflinger/buffer_item_consumer.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h - -#pragma once - -#include -#include - -#include "common/common_types.h" -#include "core/hle/service/nvflinger/consumer_base.h" -#include "core/hle/service/nvflinger/status.h" - -namespace Service::android { - -class BufferItem; - -class BufferItemConsumer final : public ConsumerBase { -public: - explicit BufferItemConsumer(std::unique_ptr consumer); - Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, - bool wait_for_fence = true); - Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence); -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp deleted file mode 100644 index 0767e548d..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// 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/logging/log.h" -#include "core/hle/service/nvdrv/core/nvmap.h" -#include "core/hle/service/nvflinger/buffer_item.h" -#include "core/hle/service/nvflinger/buffer_queue_consumer.h" -#include "core/hle/service/nvflinger/buffer_queue_core.h" -#include "core/hle/service/nvflinger/producer_listener.h" -#include "core/hle/service/nvflinger/ui/graphic_buffer.h" - -namespace Service::android { - -BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr core_, - Service::Nvidia::NvCore::NvMap& nvmap_) - : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} - -BufferQueueConsumer::~BufferQueueConsumer() = default; - -Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, - std::chrono::nanoseconds expected_present) { - std::scoped_lock lock{core->mutex}; - - // Check that the consumer doesn't currently have the maximum number of buffers acquired. - const s32 num_acquired_buffers{ - static_cast(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { - return slot.buffer_state == BufferState::Acquired; - }))}; - - if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { - LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", - num_acquired_buffers, core->max_acquired_buffer_count); - return Status::InvalidOperation; - } - - // Check if the queue is empty. - if (core->queue.empty()) { - return Status::NoBufferAvailable; - } - - auto front(core->queue.begin()); - - // If expected_present is specified, we may not want to return a buffer yet. - if (expected_present.count() != 0) { - constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second - - // The expected_present argument indicates when the buffer is expected to be presented - // on-screen. - while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { - const auto& buffer_item{core->queue[1]}; - - // If entry[1] is timely, drop entry[0] (and repeat). - const auto desired_present = buffer_item.timestamp; - if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || - desired_present > expected_present.count()) { - // This buffer is set to display in the near future, or desired_present is garbage. - LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, - expected_present.count()); - break; - } - - LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, - expected_present.count(), core->queue.size()); - - if (core->StillTracking(*front)) { - // Front buffer is still in mSlots, so mark the slot as free - slots[front->slot].buffer_state = BufferState::Free; - } - - core->queue.erase(front); - front = core->queue.begin(); - } - - // See if the front buffer is ready to be acquired. - const auto desired_present = front->timestamp; - if (desired_present > expected_present.count() && - desired_present < expected_present.count() + MAX_REASONABLE_NSEC) { - LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, - expected_present.count()); - return Status::PresentLater; - } - - LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, - expected_present.count()); - } - - const auto slot = front->slot; - *out_buffer = *front; - - LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); - - // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to - // avoid unnecessarily remapping this buffer on the consumer side. - if (out_buffer->acquire_called) { - out_buffer->graphic_buffer = nullptr; - } - - core->queue.erase(front); - - // We might have freed a slot while dropping old buffers, or the producer may be blocked - // waiting for the number of buffers in the queue to decrease. - core->SignalDequeueCondition(); - - return Status::NoError; -} - -Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) { - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot); - return Status::BadValue; - } - - std::shared_ptr listener; - { - std::scoped_lock lock{core->mutex}; - - // If the frame number has changed because the buffer has been reallocated, we can ignore - // this ReleaseBuffer for the old buffer. - if (frame_number != slots[slot].frame_number) { - return Status::StaleBufferSlot; - } - - // Make sure this buffer hasn't been queued while acquired by the consumer. - auto current(core->queue.begin()); - while (current != core->queue.end()) { - if (current->slot == slot) { - LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued", - slot); - return Status::BadValue; - } - ++current; - } - - slots[slot].buffer_state = BufferState::Free; - - nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); - - listener = core->connected_producer_listener; - - LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); - - core->SignalDequeueCondition(); - } - - // Call back without lock held - if (listener != nullptr) { - listener->OnBufferReleased(); - } - - return Status::NoError; -} - -Status BufferQueueConsumer::Connect(std::shared_ptr consumer_listener, - bool controlled_by_app) { - if (consumer_listener == nullptr) { - LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr"); - return Status::BadValue; - } - - LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - core->consumer_listener = std::move(consumer_listener); - core->consumer_controlled_by_app = controlled_by_app; - - return Status::NoError; -} - -Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { - if (out_slot_mask == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr"); - return Status::BadValue; - } - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - u64 mask = 0; - for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - if (!slots[s].acquire_called) { - mask |= (1ULL << s); - } - } - - // Remove from the mask queued buffers for which acquire has been called, since the consumer - // will not receive their buffer addresses and so must retain their cached information - auto current(core->queue.begin()); - while (current != core->queue.end()) { - if (current->acquire_called) { - mask &= ~(1ULL << current->slot); - } - ++current; - } - - LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask); - *out_slot_mask = mask; - return Status::NoError; -} - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h deleted file mode 100644 index 4ec06ca13..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h - -#pragma once - -#include -#include - -#include "common/common_types.h" -#include "core/hle/service/nvflinger/buffer_queue_defs.h" -#include "core/hle/service/nvflinger/status.h" - -namespace Service::Nvidia::NvCore { -class NvMap; -} // namespace Service::Nvidia::NvCore - -namespace Service::android { - -class BufferItem; -class BufferQueueCore; -class IConsumerListener; - -class BufferQueueConsumer final { -public: - explicit BufferQueueConsumer(std::shared_ptr core_, - Service::Nvidia::NvCore::NvMap& nvmap_); - ~BufferQueueConsumer(); - - Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); - Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); - Status Connect(std::shared_ptr consumer_listener, bool controlled_by_app); - Status GetReleasedBuffers(u64* out_slot_mask); - -private: - std::shared_ptr core; - BufferQueueDefs::SlotsType& slots; - Service::Nvidia::NvCore::NvMap& nvmap; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp deleted file mode 100644 index 3d1338e66..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp - -#include "common/assert.h" - -#include "core/hle/service/nvflinger/buffer_queue_core.h" - -namespace Service::android { - -BufferQueueCore::BufferQueueCore() = default; - -BufferQueueCore::~BufferQueueCore() = default; - -void BufferQueueCore::NotifyShutdown() { - std::scoped_lock lock{mutex}; - - is_shutting_down = true; - - SignalDequeueCondition(); -} - -void BufferQueueCore::SignalDequeueCondition() { - dequeue_possible.store(true); - dequeue_condition.notify_all(); -} - -bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock& lk) { - if (is_shutting_down) { - return false; - } - - dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); - dequeue_possible.store(false); - - return true; -} - -s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { - // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. - if (!use_async_buffer) { - return max_acquired_buffer_count; - } - - if (dequeue_buffer_cannot_block || async) { - return max_acquired_buffer_count + 1; - } - - return max_acquired_buffer_count; -} - -s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { - return GetMinUndequeuedBufferCountLocked(async) + 1; -} - -s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { - const auto min_buffer_count = GetMinMaxBufferCountLocked(async); - auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count); - - if (override_max_buffer_count != 0) { - ASSERT(override_max_buffer_count >= min_buffer_count); - max_buffer_count = override_max_buffer_count; - } - - // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed - // need to have their slots preserved. - for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { - const auto state = slots[slot].buffer_state; - if (state == BufferState::Queued || state == BufferState::Dequeued) { - max_buffer_count = slot + 1; - } - } - - return max_buffer_count; -} - -s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { - return static_cast(std::count_if(slots.begin(), slots.end(), - [](const auto& slot) { return slot.is_preallocated; })); -} - -void BufferQueueCore::FreeBufferLocked(s32 slot) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - slots[slot].graphic_buffer.reset(); - - slots[slot].buffer_state = BufferState::Free; - slots[slot].frame_number = UINT32_MAX; - slots[slot].acquire_called = false; - slots[slot].fence = Fence::NoFence(); -} - -void BufferQueueCore::FreeAllBuffersLocked() { - buffer_has_been_queued = false; - - for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { - FreeBufferLocked(slot); - } -} - -bool BufferQueueCore::StillTracking(const BufferItem& item) const { - const BufferSlot& slot = slots[item.slot]; - - return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer); -} - -void BufferQueueCore::WaitWhileAllocatingLocked() const { - while (is_allocating) { - is_allocating_condition.wait(mutex); - } -} - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h deleted file mode 100644 index 85b3bc4c1..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "core/hle/service/nvflinger/buffer_item.h" -#include "core/hle/service/nvflinger/buffer_queue_defs.h" -#include "core/hle/service/nvflinger/pixel_format.h" -#include "core/hle/service/nvflinger/status.h" -#include "core/hle/service/nvflinger/window.h" - -namespace Service::android { - -class IConsumerListener; -class IProducerListener; - -class BufferQueueCore final { - friend class BufferQueueProducer; - friend class BufferQueueConsumer; - -public: - static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; - - BufferQueueCore(); - ~BufferQueueCore(); - - void NotifyShutdown(); - -private: - void SignalDequeueCondition(); - bool WaitForDequeueCondition(std::unique_lock& lk); - - s32 GetMinUndequeuedBufferCountLocked(bool async) const; - s32 GetMinMaxBufferCountLocked(bool async) const; - s32 GetMaxBufferCountLocked(bool async) const; - s32 GetPreallocatedBufferCountLocked() const; - void FreeBufferLocked(s32 slot); - void FreeAllBuffersLocked(); - bool StillTracking(const BufferItem& item) const; - void WaitWhileAllocatingLocked() const; - -private: - mutable std::mutex mutex; - bool is_abandoned{}; - bool consumer_controlled_by_app{}; - std::shared_ptr consumer_listener; - u32 consumer_usage_bit{}; - NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi}; - std::shared_ptr connected_producer_listener; - BufferQueueDefs::SlotsType slots{}; - std::vector queue; - s32 override_max_buffer_count{}; - std::condition_variable dequeue_condition; - std::atomic dequeue_possible{}; - const bool use_async_buffer{}; // This is always disabled on HOS - bool dequeue_buffer_cannot_block{}; - PixelFormat default_buffer_format{PixelFormat::Rgba8888}; - u32 default_width{1}; - u32 default_height{1}; - s32 default_max_buffer_count{2}; - const s32 max_acquired_buffer_count{}; // This is always zero on HOS - bool buffer_has_been_queued{}; - u64 frame_counter{}; - u32 transform_hint{}; - bool is_allocating{}; - mutable std::condition_variable_any is_allocating_condition; - bool is_shutting_down{}; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h deleted file mode 100644 index 334445213..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_defs.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h - -#pragma once - -#include - -#include "common/common_types.h" -#include "core/hle/service/nvflinger/buffer_slot.h" - -namespace Service::android::BufferQueueDefs { - -// BufferQueue will keep track of at most this value of buffers. -constexpr s32 NUM_BUFFER_SLOTS = 64; - -using SlotsType = std::array; - -} // namespace Service::android::BufferQueueDefs diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp deleted file mode 100644 index ad73edd66..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ /dev/null @@ -1,933 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp - -#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/nvdrv/core/nvmap.h" -#include "core/hle/service/nvflinger/buffer_queue_core.h" -#include "core/hle/service/nvflinger/buffer_queue_producer.h" -#include "core/hle/service/nvflinger/consumer_listener.h" -#include "core/hle/service/nvflinger/parcel.h" -#include "core/hle/service/nvflinger/ui/graphic_buffer.h" -#include "core/hle/service/nvflinger/window.h" -#include "core/hle/service/vi/vi.h" - -namespace Service::android { - -BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr buffer_queue_core_, - Service::Nvidia::NvCore::NvMap& nvmap_) - : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), - nvmap(nvmap_) { - buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); -} - -BufferQueueProducer::~BufferQueueProducer() { - service_context.CloseEvent(buffer_wait_event); -} - -Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr* buf) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return Status::BadValue; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, - slots[slot].buffer_state); - return Status::BadValue; - } - - slots[slot].request_buffer_called = true; - *buf = slots[slot].graphic_buffer; - - return Status::NoError; -} - -Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { - LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); - - std::shared_ptr listener; - { - std::scoped_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return Status::BadValue; - } - - // There must be no dequeued buffers when changing the buffer count. - for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - if (slots[s].buffer_state == BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "buffer owned by producer"); - return Status::BadValue; - } - } - - if (buffer_count == 0) { - core->override_max_buffer_count = 0; - core->SignalDequeueCondition(); - return Status::NoError; - } - - const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false); - if (buffer_count < min_buffer_slots) { - LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}", - buffer_count, min_buffer_slots); - return Status::BadValue; - } - - // Here we are guaranteed that the producer doesn't have any dequeued buffers and will - // release all of its buffer references. - if (core->GetPreallocatedBufferCountLocked() <= 0) { - core->FreeAllBuffersLocked(); - } - - core->override_max_buffer_count = buffer_count; - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); - listener = core->consumer_listener; - } - - // Call back without lock held - if (listener != nullptr) { - listener->OnBuffersReleased(); - } - - return Status::NoError; -} - -Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, - std::unique_lock& lk) const { - bool try_again = true; - - while (try_again) { - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); - if (async && core->override_max_buffer_count) { - if (core->override_max_buffer_count < max_buffer_count) { - LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override"); - return Status::BadValue; - } - } - - // Free up any buffers that are in slots beyond the max buffer count - for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - ASSERT(slots[s].buffer_state == BufferState::Free); - if (slots[s].graphic_buffer != nullptr) { - core->FreeBufferLocked(s); - *return_flags |= Status::ReleaseAllBuffers; - } - } - - // Look for a free buffer to give to the client - *found = BufferQueueCore::INVALID_BUFFER_SLOT; - s32 dequeued_count{}; - s32 acquired_count{}; - for (s32 s{}; s < max_buffer_count; ++s) { - switch (slots[s].buffer_state) { - case BufferState::Dequeued: - ++dequeued_count; - break; - case BufferState::Acquired: - ++acquired_count; - break; - case BufferState::Free: - // We return the oldest of the free buffers to avoid stalling the producer if - // possible, since the consumer may still have pending reads of in-flight buffers - if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || - slots[s].frame_number < slots[*found].frame_number) { - *found = s; - } - break; - default: - break; - } - } - - // Producers are not allowed to dequeue more than one buffer if they did not set a buffer - // count - if (!core->override_max_buffer_count && dequeued_count) { - LOG_ERROR(Service_NVFlinger, - "can't dequeue multiple buffers without setting the buffer count"); - return Status::InvalidOperation; - } - - // See whether a buffer has been queued since the last SetBufferCount so we know whether to - // perform the min undequeued buffers check below - if (core->buffer_has_been_queued) { - // Make sure the producer is not trying to dequeue more buffers than allowed - const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); - const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async); - if (new_undequeued_count < min_undequeued_count) { - LOG_ERROR(Service_NVFlinger, - "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})", - min_undequeued_count, dequeued_count, new_undequeued_count); - return Status::InvalidOperation; - } - } - - // If we disconnect and reconnect quickly, we can be in a state where our slots are empty - // but we have many buffers in the queue. This can cause us to run out of memory if we - // outrun the consumer. Wait here if it looks like we have too many buffers queued up. - const bool too_many_buffers = core->queue.size() > static_cast(max_buffer_count); - if (too_many_buffers) { - LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); - } - - // If no buffer is found, or if the queue has too many buffers outstanding, wait for a - // buffer to be acquired or released, or for the max buffer count to change. - try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; - if (try_again) { - // Return an error if we're in non-blocking mode (producer and consumer are controlled - // by the application). - if (core->dequeue_buffer_cannot_block && - (acquired_count <= core->max_acquired_buffer_count)) { - return Status::WouldBlock; - } - - if (!core->WaitForDequeueCondition(lk)) { - // We are no longer running - return Status::NoError; - } - } - } - - return Status::NoError; -} - -Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width, - u32 height, PixelFormat format, u32 usage) { - LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false", - width, height, format, usage); - - if ((width != 0 && height == 0) || (width == 0 && height != 0)) { - LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height); - return Status::BadValue; - } - - Status return_flags = Status::NoError; - bool attached_by_consumer = false; - { - std::unique_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - if (format == PixelFormat::NoFormat) { - format = core->default_buffer_format; - } - - // Enable the usage bits the consumer requested - usage |= core->consumer_usage_bit; - - s32 found{}; - Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock); - if (status != Status::NoError) { - return status; - } - - // This should not happen - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - LOG_ERROR(Service_NVFlinger, "no available buffer slots"); - return Status::Busy; - } - - *out_slot = found; - - attached_by_consumer = slots[found].attached_by_consumer; - - const bool use_default_size = !width && !height; - if (use_default_size) { - width = core->default_width; - height = core->default_height; - } - - slots[found].buffer_state = BufferState::Dequeued; - - const std::shared_ptr& buffer(slots[found].graphic_buffer); - if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) || - (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) { - slots[found].acquire_called = false; - slots[found].graphic_buffer = nullptr; - slots[found].request_buffer_called = false; - slots[found].fence = Fence::NoFence(); - - return_flags |= Status::BufferNeedsReallocation; - } - - *out_fence = slots[found].fence; - slots[found].fence = Fence::NoFence(); - } - - if ((return_flags & Status::BufferNeedsReallocation) != Status::None) { - LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot); - - auto graphic_buffer = std::make_shared(width, height, format, usage); - if (graphic_buffer == nullptr) { - LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed"); - return Status::NoMemory; - } - - { - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - slots[*out_slot].frame_number = UINT32_MAX; - slots[*out_slot].graphic_buffer = graphic_buffer; - } - } - - if (attached_by_consumer) { - return_flags |= Status::BufferNeedsReallocation; - } - - LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, - slots[*out_slot].frame_number, return_flags); - - return return_flags; -} - -Status BufferQueueProducer::DetachBuffer(s32 slot) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return Status::BadValue; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, - slots[slot].buffer_state); - return Status::BadValue; - } else if (!slots[slot].request_buffer_called) { - LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot); - return Status::BadValue; - } - - core->FreeBufferLocked(slot); - core->SignalDequeueCondition(); - - return Status::NoError; -} - -Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr* out_buffer, - Fence* out_fence) { - if (out_buffer == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr"); - return Status::BadValue; - } else if (out_fence == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr"); - return Status::BadValue; - } - - std::scoped_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - // Find the oldest valid slot - int found = BufferQueueCore::INVALID_BUFFER_SLOT; - for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { - if (found == BufferQueueCore::INVALID_BUFFER_SLOT || - slots[s].frame_number < slots[found].frame_number) { - found = s; - } - } - } - - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - return Status::NoMemory; - } - - LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); - - *out_buffer = slots[found].graphic_buffer; - *out_fence = slots[found].fence; - - core->FreeBufferLocked(found); - - return Status::NoError; -} - -Status BufferQueueProducer::AttachBuffer(s32* out_slot, - const std::shared_ptr& buffer) { - if (out_slot == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr"); - return Status::BadValue; - } else if (buffer == nullptr) { - LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer"); - return Status::BadValue; - } - - std::unique_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - Status return_flags = Status::NoError; - s32 found{}; - - const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock); - if (status != Status::NoError) { - return status; - } - - // This should not happen - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - LOG_ERROR(Service_NVFlinger, "No available buffer slots"); - return Status::Busy; - } - - *out_slot = found; - - LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags); - - slots[*out_slot].graphic_buffer = buffer; - slots[*out_slot].buffer_state = BufferState::Dequeued; - slots[*out_slot].fence = Fence::NoFence(); - slots[*out_slot].request_buffer_called = true; - - return return_flags; -} - -Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, - QueueBufferOutput* output) { - s64 timestamp{}; - bool is_auto_timestamp{}; - Common::Rectangle crop; - NativeWindowScalingMode scaling_mode{}; - NativeWindowTransform transform; - u32 sticky_transform_{}; - bool async{}; - s32 swap_interval{}; - Fence fence{}; - - input.Deflate(×tamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, - &sticky_transform_, &async, &swap_interval, &fence); - - switch (scaling_mode) { - case NativeWindowScalingMode::Freeze: - case NativeWindowScalingMode::ScaleToWindow: - case NativeWindowScalingMode::ScaleCrop: - case NativeWindowScalingMode::NoScaleCrop: - break; - default: - LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode); - return Status::BadValue; - } - - std::shared_ptr frame_available_listener; - std::shared_ptr frame_replaced_listener; - s32 callback_ticket{}; - BufferItem item; - - { - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); - if (async && core->override_max_buffer_count) { - if (core->override_max_buffer_count < max_buffer_count) { - LOG_ERROR(Service_NVFlinger, "async mode is invalid with " - "buffer count override"); - return Status::BadValue; - } - } - - if (slot < 0 || slot >= max_buffer_count) { - LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, - max_buffer_count); - return Status::BadValue; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, - "slot {} is not owned by the producer " - "(state = {})", - slot, slots[slot].buffer_state); - return Status::BadValue; - } else if (!slots[slot].request_buffer_called) { - LOG_ERROR(Service_NVFlinger, - "slot {} was queued without requesting " - "a buffer", - slot); - return Status::BadValue; - } - - LOG_DEBUG(Service_NVFlinger, - "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot, - core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(), - crop.Bottom(), transform, scaling_mode); - - const std::shared_ptr& graphic_buffer(slots[slot].graphic_buffer); - Common::Rectangle buffer_rect(graphic_buffer->Width(), graphic_buffer->Height()); - Common::Rectangle cropped_rect; - [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect); - - if (cropped_rect != crop) { - LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}", - slot); - return Status::BadValue; - } - - slots[slot].fence = fence; - slots[slot].buffer_state = BufferState::Queued; - ++core->frame_counter; - slots[slot].frame_number = core->frame_counter; - - item.acquire_called = slots[slot].acquire_called; - item.graphic_buffer = slots[slot].graphic_buffer; - item.crop = crop; - item.transform = transform & ~NativeWindowTransform::InverseDisplay; - item.transform_to_display_inverse = - (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None; - item.scaling_mode = static_cast(scaling_mode); - item.timestamp = timestamp; - item.is_auto_timestamp = is_auto_timestamp; - item.frame_number = core->frame_counter; - item.slot = slot; - item.fence = fence; - item.is_droppable = core->dequeue_buffer_cannot_block || async; - item.swap_interval = swap_interval; - - nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); - - sticky_transform = sticky_transform_; - - if (core->queue.empty()) { - // When the queue is empty, we can simply queue this buffer - core->queue.push_back(item); - frame_available_listener = core->consumer_listener; - } else { - // When the queue is not empty, we need to look at the front buffer - // state to see if we need to replace it - auto front(core->queue.begin()); - - if (front->is_droppable) { - // If the front queued buffer is still being tracked, we first - // mark it as freed - if (core->StillTracking(*front)) { - slots[front->slot].buffer_state = BufferState::Free; - // Reset the frame number of the freed buffer so that it is the first in line to - // be dequeued again - slots[front->slot].frame_number = 0; - } - // Overwrite the droppable buffer with the incoming one - *front = item; - frame_replaced_listener = core->consumer_listener; - } else { - core->queue.push_back(item); - frame_available_listener = core->consumer_listener; - } - } - - core->buffer_has_been_queued = true; - core->SignalDequeueCondition(); - output->Inflate(core->default_width, core->default_height, core->transform_hint, - static_cast(core->queue.size())); - - // Take a ticket for the callback functions - callback_ticket = next_callback_ticket++; - } - - // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the - // consumer shouldn't need it - item.graphic_buffer.reset(); - item.slot = BufferItem::INVALID_BUFFER_SLOT; - - // Call back without the main BufferQueue lock held, but with the callback lock held so we can - // ensure that callbacks occur in order - { - std::scoped_lock lock{callback_mutex}; - while (callback_ticket != current_callback_ticket) { - callback_condition.wait(callback_mutex); - } - - if (frame_available_listener != nullptr) { - frame_available_listener->OnFrameAvailable(item); - } else if (frame_replaced_listener != nullptr) { - frame_replaced_listener->OnFrameReplaced(item); - } - - ++current_callback_ticket; - callback_condition.notify_all(); - } - - return Status::NoError; -} - -void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return; - } - - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, - slots[slot].buffer_state); - return; - } - - slots[slot].buffer_state = BufferState::Free; - slots[slot].frame_number = 0; - slots[slot].fence = fence; - - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); -} - -Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { - std::scoped_lock lock{core->mutex}; - - if (out_value == nullptr) { - LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); - return Status::BadValue; - } - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - u32 value{}; - switch (what) { - case NativeWindow::Width: - value = core->default_width; - break; - case NativeWindow::Height: - value = core->default_height; - break; - case NativeWindow::Format: - value = static_cast(core->default_buffer_format); - break; - case NativeWindow::MinUndequeedBuffers: - value = core->GetMinUndequeuedBufferCountLocked(false); - break; - case NativeWindow::StickyTransform: - value = sticky_transform; - break; - case NativeWindow::ConsumerRunningBehind: - value = (core->queue.size() > 1); - break; - case NativeWindow::ConsumerUsageBits: - value = core->consumer_usage_bit; - break; - default: - ASSERT(false); - return Status::BadValue; - } - - LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value); - - *out_value = static_cast(value); - - return Status::NoError; -} - -Status BufferQueueProducer::Connect(const std::shared_ptr& listener, - NativeWindowApi api, bool producer_controlled_by_app, - QueueBufferOutput* output) { - std::scoped_lock lock{core->mutex}; - - LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, - producer_controlled_by_app); - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - if (core->consumer_listener == nullptr) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer"); - return Status::NoInit; - } - - if (output == nullptr) { - LOG_ERROR(Service_NVFlinger, "output was nullptr"); - return Status::BadValue; - } - - if (core->connected_api != NativeWindowApi::NoConnectedApi) { - LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api, - api); - return Status::BadValue; - } - - Status status = Status::NoError; - switch (api) { - case NativeWindowApi::Egl: - case NativeWindowApi::Cpu: - case NativeWindowApi::Media: - case NativeWindowApi::Camera: - core->connected_api = api; - output->Inflate(core->default_width, core->default_height, core->transform_hint, - static_cast(core->queue.size())); - core->connected_producer_listener = listener; - break; - default: - LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); - status = Status::BadValue; - break; - } - - core->buffer_has_been_queued = false; - core->dequeue_buffer_cannot_block = - core->consumer_controlled_by_app && producer_controlled_by_app; - - return status; -} - -Status BufferQueueProducer::Disconnect(NativeWindowApi api) { - LOG_DEBUG(Service_NVFlinger, "api = {}", api); - - Status status = Status::NoError; - std::shared_ptr listener; - - { - std::scoped_lock lock{core->mutex}; - - core->WaitWhileAllocatingLocked(); - - if (core->is_abandoned) { - // Disconnecting after the surface has been abandoned is a no-op. - return Status::NoError; - } - - // HACK: We are not Android. Remove handle for items in queue, and clear queue. - // Allows synchronous destruction of nvmap handles. - for (auto& item : core->queue) { - nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); - } - core->queue.clear(); - - switch (api) { - case NativeWindowApi::Egl: - case NativeWindowApi::Cpu: - case NativeWindowApi::Media: - case NativeWindowApi::Camera: - if (core->connected_api == api) { - core->FreeAllBuffersLocked(); - core->connected_producer_listener = nullptr; - core->connected_api = NativeWindowApi::NoConnectedApi; - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); - listener = core->consumer_listener; - } else { - LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", - core->connected_api, api); - status = Status::BadValue; - } - break; - default: - LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); - status = Status::BadValue; - break; - } - } - - // Call back without lock held - if (listener != nullptr) { - listener->OnBuffersReleased(); - } - - return status; -} - -Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, - const std::shared_ptr& buffer) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - return Status::BadValue; - } - - std::scoped_lock lock{core->mutex}; - - slots[slot] = {}; - slots[slot].graphic_buffer = buffer; - slots[slot].frame_number = 0; - - // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for - // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. - if (buffer) { - slots[slot].is_preallocated = true; - - core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked(); - core->default_width = buffer->Width(); - core->default_height = buffer->Height(); - core->default_buffer_format = buffer->Format(); - } - - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); - - return Status::NoError; -} - -void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u32 flags) { - Status status{Status::NoError}; - InputParcel parcel_in{ctx.ReadBuffer()}; - OutputParcel parcel_out{}; - - switch (code) { - case TransactionId::Connect: { - const auto enable_listener = parcel_in.Read(); - const auto api = parcel_in.Read(); - const auto producer_controlled_by_app = parcel_in.Read(); - - UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!"); - - std::shared_ptr listener; - QueueBufferOutput output{}; - - status = Connect(listener, api, producer_controlled_by_app, &output); - - parcel_out.Write(output); - break; - } - case TransactionId::SetPreallocatedBuffer: { - const auto slot = parcel_in.Read(); - const auto buffer = parcel_in.ReadObject(); - - status = SetPreallocatedBuffer(slot, buffer); - break; - } - case TransactionId::DequeueBuffer: { - const auto is_async = parcel_in.Read(); - const auto width = parcel_in.Read(); - const auto height = parcel_in.Read(); - const auto pixel_format = parcel_in.Read(); - const auto usage = parcel_in.Read(); - - s32 slot{}; - Fence fence{}; - - status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage); - - parcel_out.Write(slot); - parcel_out.WriteObject(&fence); - break; - } - case TransactionId::RequestBuffer: { - const auto slot = parcel_in.Read(); - - std::shared_ptr buf; - - status = RequestBuffer(slot, &buf); - - parcel_out.WriteObject(buf); - break; - } - case TransactionId::QueueBuffer: { - const auto slot = parcel_in.Read(); - - QueueBufferInput input{parcel_in}; - QueueBufferOutput output; - - status = QueueBuffer(slot, input, &output); - - parcel_out.Write(output); - break; - } - case TransactionId::Query: { - const auto what = parcel_in.Read(); - - s32 value{}; - - status = Query(what, &value); - - parcel_out.Write(value); - break; - } - case TransactionId::CancelBuffer: { - const auto slot = parcel_in.Read(); - const auto fence = parcel_in.ReadFlattened(); - - CancelBuffer(slot, fence); - break; - } - case TransactionId::Disconnect: { - const auto api = parcel_in.Read(); - - status = Disconnect(api); - break; - } - case TransactionId::DetachBuffer: { - const auto slot = parcel_in.Read(); - - status = DetachBuffer(slot); - break; - } - case TransactionId::SetBufferCount: { - const auto buffer_count = parcel_in.Read(); - - status = SetBufferCount(buffer_count); - break; - } - case TransactionId::GetBufferHistory: - LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory"); - break; - default: - ASSERT_MSG(false, "Unimplemented TransactionId {}", code); - break; - } - - parcel_out.Write(status); - - ctx.WriteBuffer(parcel_out.Serialize()); -} - -Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { - return buffer_wait_event->GetReadableEvent(); -} - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h deleted file mode 100644 index 16189fa6f..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h - -#pragma once - -#include -#include -#include - -#include "common/common_funcs.h" -#include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/binder.h" -#include "core/hle/service/nvflinger/buffer_queue_defs.h" -#include "core/hle/service/nvflinger/buffer_slot.h" -#include "core/hle/service/nvflinger/graphic_buffer_producer.h" -#include "core/hle/service/nvflinger/pixel_format.h" -#include "core/hle/service/nvflinger/status.h" -#include "core/hle/service/nvflinger/window.h" - -namespace Kernel { -class KernelCore; -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Service::KernelHelpers { -class ServiceContext; -} // namespace Service::KernelHelpers - -namespace Service::Nvidia::NvCore { -class NvMap; -} // namespace Service::Nvidia::NvCore - -namespace Service::android { - -class BufferQueueCore; -class IProducerListener; - -class BufferQueueProducer final : public IBinder { -public: - explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr buffer_queue_core_, - Service::Nvidia::NvCore::NvMap& nvmap_); - ~BufferQueueProducer(); - - void Transact(HLERequestContext& ctx, android::TransactionId code, u32 flags) override; - - Kernel::KReadableEvent& GetNativeHandle() override; - -public: - Status RequestBuffer(s32 slot, std::shared_ptr* buf); - Status SetBufferCount(s32 buffer_count); - Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width, - u32 height, PixelFormat format, u32 usage); - Status DetachBuffer(s32 slot); - Status DetachNextBuffer(std::shared_ptr* out_buffer, Fence* out_fence); - Status AttachBuffer(s32* outSlot, const std::shared_ptr& buffer); - Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output); - void CancelBuffer(s32 slot, const Fence& fence); - Status Query(NativeWindow what, s32* out_value); - Status Connect(const std::shared_ptr& listener, NativeWindowApi api, - bool producer_controlled_by_app, QueueBufferOutput* output); - - Status Disconnect(NativeWindowApi api); - Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr& buffer); - -private: - BufferQueueProducer(const BufferQueueProducer&) = delete; - - Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, - std::unique_lock& lk) const; - - Kernel::KEvent* buffer_wait_event{}; - Service::KernelHelpers::ServiceContext& service_context; - - std::shared_ptr core; - BufferQueueDefs::SlotsType& slots; - u32 sticky_transform{}; - std::mutex callback_mutex; - s32 next_callback_ticket{}; - s32 current_callback_ticket{}; - std::condition_variable_any callback_condition; - - Service::Nvidia::NvCore::NvMap& nvmap; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h deleted file mode 100644 index 0cd0e9964..000000000 --- a/src/core/hle/service/nvflinger/buffer_slot.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h - -#pragma once - -#include - -#include "common/common_types.h" -#include "core/hle/service/nvflinger/ui/fence.h" - -namespace Service::android { - -class GraphicBuffer; - -enum class BufferState : u32 { - Free = 0, - Dequeued = 1, - Queued = 2, - Acquired = 3, -}; - -struct BufferSlot final { - constexpr BufferSlot() = default; - - std::shared_ptr graphic_buffer; - BufferState buffer_state{BufferState::Free}; - bool request_buffer_called{}; - u64 frame_number{}; - Fence fence; - bool acquire_called{}; - bool attached_by_consumer{}; - bool is_preallocated{}; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h deleted file mode 100644 index 67aa5dad6..000000000 --- a/src/core/hle/service/nvflinger/buffer_transform_flags.h +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" - -namespace Service::android { - -enum class BufferTransformFlags : u32 { - /// No transform flags are set - Unset = 0x00, - /// Flip source image horizontally (around the vertical axis) - FlipH = 0x01, - /// Flip source image vertically (around the horizontal axis) - FlipV = 0x02, - /// Rotate source image 90 degrees clockwise - Rotate90 = 0x04, - /// Rotate source image 180 degrees - Rotate180 = 0x03, - /// Rotate source image 270 degrees clockwise - Rotate270 = 0x07, -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp deleted file mode 100644 index 982531e2d..000000000 --- a/src/core/hle/service/nvflinger/consumer_base.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/hle/service/nvflinger/buffer_item.h" -#include "core/hle/service/nvflinger/buffer_queue_consumer.h" -#include "core/hle/service/nvflinger/buffer_queue_core.h" -#include "core/hle/service/nvflinger/consumer_base.h" -#include "core/hle/service/nvflinger/ui/graphic_buffer.h" - -namespace Service::android { - -ConsumerBase::ConsumerBase(std::unique_ptr consumer_) - : consumer{std::move(consumer_)} {} - -ConsumerBase::~ConsumerBase() { - std::scoped_lock lock{mutex}; - - ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); -} - -void ConsumerBase::Connect(bool controlled_by_app) { - consumer->Connect(shared_from_this(), controlled_by_app); -} - -void ConsumerBase::FreeBufferLocked(s32 slot_index) { - LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index); - - slots[slot_index].graphic_buffer = nullptr; - slots[slot_index].fence = Fence::NoFence(); - slots[slot_index].frame_number = 0; -} - -void ConsumerBase::OnFrameAvailable(const BufferItem& item) { - LOG_DEBUG(Service_NVFlinger, "called"); -} - -void ConsumerBase::OnFrameReplaced(const BufferItem& item) { - LOG_DEBUG(Service_NVFlinger, "called"); -} - -void ConsumerBase::OnBuffersReleased() { - std::scoped_lock lock{mutex}; - - LOG_DEBUG(Service_NVFlinger, "called"); - - if (is_abandoned) { - // Nothing to do if we're already abandoned. - return; - } - - u64 mask = 0; - consumer->GetReleasedBuffers(&mask); - for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { - if (mask & (1ULL << i)) { - FreeBufferLocked(i); - } - } -} - -void ConsumerBase::OnSidebandStreamChanged() {} - -Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) { - Status err = consumer->AcquireBuffer(item, present_when); - if (err != Status::NoError) { - return err; - } - - if (item->graphic_buffer != nullptr) { - slots[item->slot].graphic_buffer = item->graphic_buffer; - } - - slots[item->slot].frame_number = item->frame_number; - slots[item->slot].fence = item->fence; - - LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot); - - return Status::NoError; -} - -Status ConsumerBase::AddReleaseFenceLocked(s32 slot, - const std::shared_ptr& graphic_buffer, - const Fence& fence) { - LOG_DEBUG(Service_NVFlinger, "slot={}", slot); - - // If consumer no longer tracks this graphic_buffer, we can safely - // drop this fence, as it will never be received by the producer. - - if (!StillTracking(slot, graphic_buffer)) { - return Status::NoError; - } - - slots[slot].fence = fence; - - return Status::NoError; -} - -Status ConsumerBase::ReleaseBufferLocked(s32 slot, - const std::shared_ptr& graphic_buffer) { - // If consumer no longer tracks this graphic_buffer (we received a new - // buffer on the same slot), the buffer producer is definitely no longer - // tracking it. - - if (!StillTracking(slot, graphic_buffer)) { - return Status::NoError; - } - - LOG_DEBUG(Service_NVFlinger, "slot={}", slot); - Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence); - if (err == Status::StaleBufferSlot) { - FreeBufferLocked(slot); - } - - slots[slot].fence = Fence::NoFence(); - - return err; -} - -bool ConsumerBase::StillTracking(s32 slot, - const std::shared_ptr& graphic_buffer) const { - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - return false; - } - - return (slots[slot].graphic_buffer != nullptr && - slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle()); -} - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h deleted file mode 100644 index 9a8a5f6bb..000000000 --- a/src/core/hle/service/nvflinger/consumer_base.h +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h - -#pragma once - -#include -#include -#include -#include - -#include "common/common_types.h" -#include "core/hle/service/nvflinger/buffer_queue_defs.h" -#include "core/hle/service/nvflinger/consumer_listener.h" -#include "core/hle/service/nvflinger/status.h" - -namespace Service::android { - -class BufferItem; -class BufferQueueConsumer; - -class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this { -public: - void Connect(bool controlled_by_app); - -protected: - explicit ConsumerBase(std::unique_ptr consumer_); - ~ConsumerBase() override; - - void OnFrameAvailable(const BufferItem& item) override; - void OnFrameReplaced(const BufferItem& item) override; - void OnBuffersReleased() override; - void OnSidebandStreamChanged() override; - - void FreeBufferLocked(s32 slot_index); - Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); - Status ReleaseBufferLocked(s32 slot, const std::shared_ptr& graphic_buffer); - bool StillTracking(s32 slot, const std::shared_ptr& graphic_buffer) const; - Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr& graphic_buffer, - const Fence& fence); - - struct Slot final { - std::shared_ptr graphic_buffer; - Fence fence; - u64 frame_number{}; - }; - -protected: - std::array slots; - - bool is_abandoned{}; - - std::unique_ptr consumer; - - mutable std::mutex mutex; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h deleted file mode 100644 index 74a193988..000000000 --- a/src/core/hle/service/nvflinger/consumer_listener.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h - -#pragma once - -namespace Service::android { - -class BufferItem; - -/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events -/// that the consumer may wish to react to. -class IConsumerListener { -public: - IConsumerListener() = default; - virtual ~IConsumerListener() = default; - - virtual void OnFrameAvailable(const BufferItem& item) = 0; - virtual void OnFrameReplaced(const BufferItem& item) = 0; - virtual void OnBuffersReleased() = 0; - virtual void OnSidebandStreamChanged() = 0; -}; - -}; // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp deleted file mode 100644 index 769e8c0a3..000000000 --- a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp - -#include "core/hle/service/nvflinger/graphic_buffer_producer.h" -#include "core/hle/service/nvflinger/parcel.h" - -namespace Service::android { - -QueueBufferInput::QueueBufferInput(InputParcel& parcel) { - parcel.ReadFlattened(*this); -} - -QueueBufferOutput::QueueBufferOutput() = default; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h deleted file mode 100644 index 2969f0fd5..000000000 --- a/src/core/hle/service/nvflinger/graphic_buffer_producer.h +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/math_util.h" -#include "core/hle/service/nvflinger/ui/fence.h" -#include "core/hle/service/nvflinger/window.h" - -namespace Service::android { - -class InputParcel; - -#pragma pack(push, 1) -struct QueueBufferInput final { - explicit QueueBufferInput(InputParcel& parcel); - - void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle* crop_, - NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, - u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const { - *timestamp_ = timestamp; - *is_auto_timestamp_ = static_cast(is_auto_timestamp); - *crop_ = crop; - *scaling_mode_ = scaling_mode; - *transform_ = transform; - *sticky_transform_ = sticky_transform; - *async_ = static_cast(async); - *swap_interval_ = swap_interval; - *fence_ = fence; - } - -private: - s64 timestamp{}; - s32 is_auto_timestamp{}; - Common::Rectangle crop{}; - NativeWindowScalingMode scaling_mode{}; - NativeWindowTransform transform{}; - u32 sticky_transform{}; - s32 async{}; - s32 swap_interval{}; - Fence fence{}; -}; -#pragma pack(pop) -static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size"); - -struct QueueBufferOutput final { - QueueBufferOutput(); - - void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const { - *width_ = width; - *height_ = height; - *transform_hint_ = transform_hint; - *num_pending_buffers_ = num_pending_buffers; - } - - void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) { - width = width_; - height = height_; - transform_hint = transform_hint_; - num_pending_buffers = num_pending_buffers_; - } - -private: - u32 width{}; - u32 height{}; - u32 transform_hint{}; - u32 num_pending_buffers{}; -}; -static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size"); - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp deleted file mode 100644 index dc9b2a9ec..000000000 --- a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include - -#include "common/common_types.h" -#include "core/hle/service/nvflinger/hos_binder_driver_server.h" - -namespace Service::NVFlinger { - -HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) - : service_context(system_, "HosBinderDriverServer") {} - -HosBinderDriverServer::~HosBinderDriverServer() {} - -u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr&& binder) { - std::scoped_lock lk{lock}; - - last_id++; - - producers[last_id] = std::move(binder); - - return last_id; -} - -android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { - std::scoped_lock lk{lock}; - - if (auto search = producers.find(id); search != producers.end()) { - return search->second.get(); - } - - return {}; -} - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h deleted file mode 100644 index 8fddc1206..000000000 --- a/src/core/hle/service/nvflinger/hos_binder_driver_server.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include - -#include "common/common_types.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvflinger/binder.h" - -namespace Core { -class System; -} - -namespace Service::NVFlinger { - -class HosBinderDriverServer final { -public: - explicit HosBinderDriverServer(Core::System& system_); - ~HosBinderDriverServer(); - - u64 RegisterProducer(std::unique_ptr&& binder); - - android::IBinder* TryGetProducer(u64 id); - -private: - KernelHelpers::ServiceContext service_context; - - std::unordered_map> producers; - std::mutex lock; - u64 last_id{}; -}; - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp deleted file mode 100644 index f4416f5b2..000000000 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/microprofile.h" -#include "common/scope_exit.h" -#include "common/settings.h" -#include "common/thread.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvflinger/buffer_item_consumer.h" -#include "core/hle/service/nvflinger/buffer_queue_core.h" -#include "core/hle/service/nvflinger/hos_binder_driver_server.h" -#include "core/hle/service/nvflinger/nvflinger.h" -#include "core/hle/service/nvflinger/ui/graphic_buffer.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" -#include "video_core/gpu.h" -#include "video_core/host1x/host1x.h" -#include "video_core/host1x/syncpoint_manager.h" - -namespace Service::NVFlinger { - -constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; - -void NVFlinger::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(false); - vsync_signal.store(false); - - guard->lock(); - - Compose(); - - guard->unlock(); - } -} - -NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_) - : system(system_), service_context(system_, "nvflinger"), - 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(); - - // Schedule the screen composition events - multi_composition_event = Core::Timing::CreateEvent( - "ScreenComposition", - [this](std::uintptr_t, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - vsync_signal.store(true); - vsync_signal.notify_all(); - return std::chrono::nanoseconds(GetNextTicks()); - }); - - single_composition_event = Core::Timing::CreateEvent( - "ScreenComposition", - [this](std::uintptr_t, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - 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); - } -} - -NVFlinger::~NVFlinger() { - if (system.IsMulticore()) { - system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); - vsync_thread.request_stop(); - vsync_signal.store(true); - vsync_signal.notify_all(); - } else { - system.CoreTiming().UnscheduleEvent(single_composition_event, {}); - } - - ShutdownLayers(); - - if (nvdrv) { - nvdrv->Close(disp_fd); - } -} - -void NVFlinger::ShutdownLayers() { - for (auto& display : displays) { - for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { - display.GetLayer(layer).Core().NotifyShutdown(); - } - } -} - -void NVFlinger::SetNVDrvInstance(std::shared_ptr instance) { - nvdrv = std::move(instance); - disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); -} - -std::optional NVFlinger::OpenDisplay(std::string_view name) { - const auto lock_guard = Lock(); - - LOG_DEBUG(Service_NVFlinger, "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 NVFlinger::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 NVFlinger::CreateLayer(u64 display_id) { - 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); - return layer_id; -} - -void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { - const auto buffer_id = next_buffer_queue_id++; - display.CreateLayer(layer_id, buffer_id, nvdrv->container); -} - -void NVFlinger::CloseLayer(u64 layer_id) { - const auto lock_guard = Lock(); - - for (auto& display : displays) { - display.CloseLayer(layer_id); - } -} - -std::optional NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { - const auto lock_guard = Lock(); - const auto* const layer = FindOrCreateLayer(display_id, layer_id); - - if (layer == nullptr) { - return std::nullopt; - } - - return layer->GetBinderId(); -} - -ResultVal NVFlinger::FindVsyncEvent(u64 display_id) { - const auto lock_guard = Lock(); - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return VI::ResultNotFound; - } - - return display->GetVSyncEvent(); -} - -VI::Display* NVFlinger::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* NVFlinger::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* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return nullptr; - } - - return display->FindLayer(layer_id); -} - -const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { - const auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return nullptr; - } - - return display->FindLayer(layer_id); -} - -VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return nullptr; - } - - auto* layer = display->FindLayer(layer_id); - - if (layer == nullptr) { - LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id); - CreateLayerAtId(*display, layer_id); - return display->FindLayer(layer_id); - } - - return layer; -} - -void NVFlinger::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; - - // TODO(Subv): Support more than 1 layer. - VI::Layer& layer = display.GetLayer(0); - - android::BufferItem buffer{}; - const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); - - if (status != android::Status::NoError) { - continue; - } - - const auto& igbp_buffer = *buffer.graphic_buffer; - - if (!system.IsPoweredOn()) { - return; // We are likely shutting down - } - - // Now send the buffer to the GPU for drawing. - // TODO(Subv): Support more than just disp0. The display device selection is probably based - // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice(disp_fd); - ASSERT(nvdisp); - - guard->unlock(); - Common::Rectangle crop_rect{ - static_cast(buffer.crop.Left()), static_cast(buffer.crop.Top()), - static_cast(buffer.crop.Right()), static_cast(buffer.crop.Bottom())}; - - nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), - igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), - static_cast(buffer.transform), crop_rect, - buffer.fence.fences, buffer.fence.num_fences); - - MicroProfileFlip(); - guard->lock(); - - swap_interval = buffer.swap_interval; - - layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); - } -} - -s64 NVFlinger::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; - } - } - - // As an extension, treat nonpositive swap interval as framerate multiplier. - const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast(1 - swap_interval) - : 60.f / static_cast(swap_interval); - - return static_cast(speed_scale * (1000000000.f / effective_fps)); -} - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h deleted file mode 100644 index 3828cf272..000000000 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "common/common_types.h" -#include "common/polyfill_thread.h" -#include "core/hle/result.h" -#include "core/hle/service/kernel_helpers.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 Layer; -} // namespace Service::VI - -namespace Service::android { -class BufferQueueCore; -class BufferQueueProducer; -} // namespace Service::android - -namespace Service::NVFlinger { - -class NVFlinger final { -public: - explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); - ~NVFlinger(); - - void ShutdownLayers(); - - /// Sets the NVDrv module instance to use to send buffers to the GPU. - void SetNVDrvInstance(std::shared_ptr instance); - - /// Opens the specified display and returns the ID. - /// - /// If an invalid display name is provided, then an empty optional is returned. - [[nodiscard]] std::optional 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 CreateLayer(u64 display_id); - - /// Closes a layer on all displays for the given layer ID. - void CloseLayer(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 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]] ResultVal FindVsyncEvent(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: - struct Layer { - std::unique_ptr core; - std::unique_ptr producer; - }; - -private: - [[nodiscard]] std::unique_lock 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); - - /// Finds the layer identified by the specified ID in the desired display. - [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; - - /// Finds the layer identified by the specified ID in the desired display, - /// or creates the layer if it is not found. - /// To be used when the system expects the specified ID to already exist. - [[nodiscard]] VI::Layer* FindOrCreateLayer(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); - - void SplitVSync(std::stop_token stop_token); - - std::shared_ptr nvdrv; - s32 disp_fd; - - std::list 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; - - /// Event that handles screen composition. - std::shared_ptr multi_composition_event; - std::shared_ptr single_composition_event; - - std::shared_ptr guard; - - Core::System& system; - - std::atomic vsync_signal; - - std::jthread vsync_thread; - - KernelHelpers::ServiceContext service_context; - - HosBinderDriverServer& hos_binder_driver_server; -}; - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h deleted file mode 100644 index d1b6201e0..000000000 --- a/src/core/hle/service/nvflinger/parcel.h +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/common_types.h" - -namespace Service::android { - -struct ParcelHeader { - u32 data_size; - u32 data_offset; - u32 objects_size; - u32 objects_offset; -}; -static_assert(sizeof(ParcelHeader) == 16, "ParcelHeader has wrong size"); - -class InputParcel final { -public: - explicit InputParcel(std::span in_data) : read_buffer(std::move(in_data)) { - DeserializeHeader(); - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - } - - template - void Read(T& val) { - static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= read_buffer.size()); - - std::memcpy(&val, read_buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - read_index = Common::AlignUp(read_index, 4); - } - - template - T Read() { - T val; - Read(val); - return val; - } - - template - void ReadFlattened(T& val) { - const auto flattened_size = Read(); - ASSERT(sizeof(T) == flattened_size); - Read(val); - } - - template - T ReadFlattened() { - T val; - ReadFlattened(val); - return val; - } - - template - T ReadUnaligned() { - static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= read_buffer.size()); - - T val; - std::memcpy(&val, read_buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - return val; - } - - template - const std::shared_ptr ReadObject() { - static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); - - const auto is_valid{Read()}; - - if (is_valid) { - auto result = std::make_shared(); - ReadFlattened(*result); - return result; - } - - return {}; - } - - std::u16string ReadInterfaceToken() { - [[maybe_unused]] const u32 unknown = Read(); - const u32 length = Read(); - - std::u16string token; - token.reserve(length + 1); - - for (u32 ch = 0; ch < length + 1; ++ch) { - token.push_back(ReadUnaligned()); - } - - read_index = Common::AlignUp(read_index, 4); - - return token; - } - - void DeserializeHeader() { - ASSERT(read_buffer.size() > sizeof(ParcelHeader)); - - ParcelHeader header{}; - std::memcpy(&header, read_buffer.data(), sizeof(ParcelHeader)); - - read_index = header.data_offset; - } - -private: - std::span read_buffer; - std::size_t read_index = 0; -}; - -class OutputParcel final { -public: - static constexpr std::size_t DefaultBufferSize = 0x40; - - OutputParcel() : buffer(DefaultBufferSize) {} - - template - explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) { - Write(out_data); - } - - template - void Write(const T& val) { - static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); - - if (buffer.size() < write_index + sizeof(T)) { - buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); - } - - std::memcpy(buffer.data() + write_index, &val, sizeof(T)); - write_index += sizeof(T); - write_index = Common::AlignUp(write_index, 4); - } - - template - void WriteObject(const T* ptr) { - static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); - - if (!ptr) { - Write(0); - return; - } - - Write(1); - Write(sizeof(T)); - Write(*ptr); - } - - template - void WriteObject(const std::shared_ptr ptr) { - WriteObject(ptr.get()); - } - - std::vector Serialize() const { - ParcelHeader header{}; - header.data_size = static_cast(write_index - sizeof(ParcelHeader)); - header.data_offset = sizeof(ParcelHeader); - header.objects_size = 4; - header.objects_offset = static_cast(sizeof(ParcelHeader) + header.data_size); - std::memcpy(buffer.data(), &header, sizeof(ParcelHeader)); - - return buffer; - } - -private: - mutable std::vector buffer; - std::size_t write_index = sizeof(ParcelHeader); -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h deleted file mode 100644 index f77d0acfb..000000000 --- a/src/core/hle/service/nvflinger/pixel_format.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" - -namespace Service::android { - -enum class PixelFormat : u32 { - NoFormat = 0, - Rgba8888 = 1, - Rgbx8888 = 2, - Rgb888 = 3, - Rgb565 = 4, - Bgra8888 = 5, - Rgba5551 = 6, - Rgba4444 = 7, -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h deleted file mode 100644 index 6bf8aaf1e..000000000 --- a/src/core/hle/service/nvflinger/producer_listener.h +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h - -#pragma once - -namespace Service::android { - -class IProducerListener { -public: - virtual ~IProducerListener() = default; - virtual void OnBufferReleased() = 0; -}; - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h deleted file mode 100644 index 7af166c40..000000000 --- a/src/core/hle/service/nvflinger/status.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" - -namespace Service::android { - -enum class Status : s32 { - None = 0, - NoError = 0, - StaleBufferSlot = 1, - NoBufferAvailable = 2, - PresentLater = 3, - WouldBlock = -11, - NoMemory = -12, - Busy = -16, - NoInit = -19, - BadValue = -22, - InvalidOperation = -37, - BufferNeedsReallocation = 1, - ReleaseAllBuffers = 2, -}; -DECLARE_ENUM_FLAG_OPERATORS(Status); - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h deleted file mode 100644 index 536e8156d..000000000 --- a/src/core/hle/service/nvflinger/ui/fence.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h - -#pragma once - -#include - -#include "common/common_types.h" -#include "core/hle/service/nvdrv/nvdata.h" - -namespace Service::android { - -class Fence { -public: - constexpr Fence() = default; - - static constexpr Fence NoFence() { - Fence fence; - fence.fences[0].id = -1; - return fence; - } - -public: - u32 num_fences{}; - std::array fences{}; -}; -static_assert(sizeof(Fence) == 36, "Fence has wrong size"); - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h deleted file mode 100644 index 9a27f8f02..000000000 --- a/src/core/hle/service/nvflinger/ui/graphic_buffer.h +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project -// SPDX-License-Identifier: GPL-3.0-or-later -// Parts of this implementation were based on: -// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hle/service/nvflinger/pixel_format.h" - -namespace Service::android { - -class GraphicBuffer final { -public: - constexpr GraphicBuffer() = default; - - constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) - : width{static_cast(width_)}, height{static_cast(height_)}, format{format_}, - usage{static_cast(usage_)} {} - - constexpr u32 Width() const { - return static_cast(width); - } - - constexpr u32 Height() const { - return static_cast(height); - } - - constexpr u32 Stride() const { - return static_cast(stride); - } - - constexpr u32 Usage() const { - return static_cast(usage); - } - - constexpr PixelFormat Format() const { - return format; - } - - constexpr u32 BufferId() const { - return buffer_id; - } - - constexpr PixelFormat ExternalFormat() const { - return external_format; - } - - constexpr u32 Handle() const { - return handle; - } - - constexpr u32 Offset() const { - return offset; - } - - constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_, - u32 usage_) const { - if (static_cast(width_) != width) { - return true; - } - - if (static_cast(height_) != height) { - return true; - } - - if (format_ != format) { - return true; - } - - if ((static_cast(usage) & usage_) != usage_) { - return true; - } - - return false; - } - -private: - u32 magic{}; - s32 width{}; - s32 height{}; - s32 stride{}; - PixelFormat format{}; - s32 usage{}; - INSERT_PADDING_WORDS(1); - u32 index{}; - INSERT_PADDING_WORDS(3); - u32 buffer_id{}; - INSERT_PADDING_WORDS(6); - PixelFormat external_format{}; - INSERT_PADDING_WORDS(10); - u32 handle{}; - u32 offset{}; - INSERT_PADDING_WORDS(60); -}; -static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); - -} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h deleted file mode 100644 index 61cca5b01..000000000 --- a/src/core/hle/service/nvflinger/window.h +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" - -namespace Service::android { - -/// Attributes queryable with Query -enum class NativeWindow : s32 { - Width = 0, - Height = 1, - Format = 2, - MinUndequeedBuffers = 3, - QueuesToWindowComposer = 4, - ConcreteType = 5, - DefaultWidth = 6, - DefaultHeight = 7, - TransformHint = 8, - ConsumerRunningBehind = 9, - ConsumerUsageBits = 10, - StickyTransform = 11, - DefaultDataSpace = 12, - BufferAge = 13, -}; - -/// Parameter for Connect/Disconnect -enum class NativeWindowApi : s32 { - NoConnectedApi = 0, - Egl = 1, - Cpu = 2, - Media = 3, - Camera = 4, -}; - -/// Scaling mode parameter for QueueBuffer -enum class NativeWindowScalingMode : s32 { - Freeze = 0, - ScaleToWindow = 1, - ScaleCrop = 2, - NoScaleCrop = 3, -}; - -/// Transform parameter for QueueBuffer -enum class NativeWindowTransform : u32 { - None = 0x0, - InverseDisplay = 0x08, -}; -DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform); - -} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/binder.h b/src/core/hle/service/nvnflinger/binder.h new file mode 100644 index 000000000..aef1477e3 --- /dev/null +++ b/src/core/hle/service/nvnflinger/binder.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h + +#pragma once + +#include "common/common_types.h" + +namespace Kernel { +class KReadableEvent; +} // namespace Kernel + +namespace Service { +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(HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0; + virtual Kernel::KReadableEvent& GetNativeHandle() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h new file mode 100644 index 000000000..7fd808f54 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_item.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h + +#pragma once + +#include + +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvnflinger/ui/fence.h" +#include "core/hle/service/nvnflinger/window.h" + +namespace Service::android { + +class GraphicBuffer; + +class BufferItem final { +public: + constexpr BufferItem() = default; + + std::shared_ptr graphic_buffer; + Fence fence; + Common::Rectangle crop; + NativeWindowTransform transform{}; + u32 scaling_mode{}; + s64 timestamp{}; + bool is_auto_timestamp{}; + u64 frame_number{}; + + // The default value for buf, used to indicate this doesn't correspond to a slot. + static constexpr s32 INVALID_BUFFER_SLOT = -1; + union { + s32 slot{INVALID_BUFFER_SLOT}; + s32 buf; + }; + + bool is_droppable{}; + bool acquire_called{}; + bool transform_to_display_inverse{}; + s32 swap_interval{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp new file mode 100644 index 000000000..cf151ea3a --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvnflinger/buffer_item.h" +#include "core/hle/service/nvnflinger/buffer_item_consumer.h" +#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" + +namespace Service::android { + +BufferItemConsumer::BufferItemConsumer(std::unique_ptr consumer_) + : ConsumerBase{std::move(consumer_)} {} + +Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence) { + if (!item) { + return Status::BadValue; + } + + std::scoped_lock lock{mutex}; + + if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { + if (status != Status::NoBufferAvailable) { + LOG_ERROR(Service_Nvnflinger, "Failed to acquire buffer: {}", status); + } + return status; + } + + if (wait_for_fence) { + UNIMPLEMENTED(); + } + + item->graphic_buffer = slots[item->slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, const Fence& release_fence) { + std::scoped_lock lock{mutex}; + + if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); + status != Status::NoError) { + LOG_ERROR(Service_Nvnflinger, "Failed to add fence: {}", status); + } + + if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer); + status != Status::NoError) { + LOG_WARNING(Service_Nvnflinger, "Failed to release buffer: {}", status); + return status; + } + + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.h b/src/core/hle/service/nvnflinger/buffer_item_consumer.h new file mode 100644 index 000000000..e0c6b3604 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/consumer_base.h" +#include "core/hle/service/nvnflinger/status.h" + +namespace Service::android { + +class BufferItem; + +class BufferItemConsumer final : public ConsumerBase { +public: + explicit BufferItemConsumer(std::unique_ptr consumer); + Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence = true); + Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp new file mode 100644 index 000000000..51291539d --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// 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/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.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/producer_listener.h" +#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" + +namespace Service::android { + +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} + +BufferQueueConsumer::~BufferQueueConsumer() = default; + +Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, + std::chrono::nanoseconds expected_present) { + std::scoped_lock lock{core->mutex}; + + // Check that the consumer doesn't currently have the maximum number of buffers acquired. + const s32 num_acquired_buffers{ + static_cast(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { + return slot.buffer_state == BufferState::Acquired; + }))}; + + if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { + LOG_ERROR(Service_Nvnflinger, "max acquired buffer count reached: {} (max {})", + num_acquired_buffers, core->max_acquired_buffer_count); + return Status::InvalidOperation; + } + + // Check if the queue is empty. + if (core->queue.empty()) { + return Status::NoBufferAvailable; + } + + auto front(core->queue.begin()); + + // If expected_present is specified, we may not want to return a buffer yet. + if (expected_present.count() != 0) { + constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second + + // The expected_present argument indicates when the buffer is expected to be presented + // on-screen. + while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { + const auto& buffer_item{core->queue[1]}; + + // If entry[1] is timely, drop entry[0] (and repeat). + const auto desired_present = buffer_item.timestamp; + if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || + desired_present > expected_present.count()) { + // This buffer is set to display in the near future, or desired_present is garbage. + LOG_DEBUG(Service_Nvnflinger, "nodrop desire={} expect={}", desired_present, + expected_present.count()); + break; + } + + LOG_DEBUG(Service_Nvnflinger, "drop desire={} expect={} size={}", desired_present, + expected_present.count(), core->queue.size()); + + if (core->StillTracking(*front)) { + // Front buffer is still in mSlots, so mark the slot as free + slots[front->slot].buffer_state = BufferState::Free; + } + + core->queue.erase(front); + front = core->queue.begin(); + } + + // See if the front buffer is ready to be acquired. + const auto desired_present = front->timestamp; + if (desired_present > expected_present.count() && + desired_present < expected_present.count() + MAX_REASONABLE_NSEC) { + LOG_DEBUG(Service_Nvnflinger, "defer desire={} expect={}", desired_present, + expected_present.count()); + return Status::PresentLater; + } + + LOG_DEBUG(Service_Nvnflinger, "accept desire={} expect={}", desired_present, + expected_present.count()); + } + + const auto slot = front->slot; + *out_buffer = *front; + + LOG_DEBUG(Service_Nvnflinger, "acquiring slot={}", slot); + + // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to + // avoid unnecessarily remapping this buffer on the consumer side. + if (out_buffer->acquire_called) { + out_buffer->graphic_buffer = nullptr; + } + + core->queue.erase(front); + + // We might have freed a slot while dropping old buffers, or the producer may be blocked + // waiting for the number of buffers in the queue to decrease. + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_Nvnflinger, "slot {} out of range", slot); + return Status::BadValue; + } + + std::shared_ptr listener; + { + std::scoped_lock lock{core->mutex}; + + // If the frame number has changed because the buffer has been reallocated, we can ignore + // this ReleaseBuffer for the old buffer. + if (frame_number != slots[slot].frame_number) { + return Status::StaleBufferSlot; + } + + // Make sure this buffer hasn't been queued while acquired by the consumer. + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->slot == slot) { + LOG_ERROR(Service_Nvnflinger, "buffer slot {} pending release is currently queued", + slot); + return Status::BadValue; + } + ++current; + } + + slots[slot].buffer_state = BufferState::Free; + + nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); + + listener = core->connected_producer_listener; + + LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); + + core->SignalDequeueCondition(); + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBufferReleased(); + } + + return Status::NoError; +} + +Status BufferQueueConsumer::Connect(std::shared_ptr consumer_listener, + bool controlled_by_app) { + if (consumer_listener == nullptr) { + LOG_ERROR(Service_Nvnflinger, "consumer_listener may not be nullptr"); + return Status::BadValue; + } + + LOG_DEBUG(Service_Nvnflinger, "controlled_by_app={}", controlled_by_app); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + core->consumer_listener = std::move(consumer_listener); + core->consumer_controlled_by_app = controlled_by_app; + + return Status::NoError; +} + +Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { + if (out_slot_mask == nullptr) { + LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u64 mask = 0; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (!slots[s].acquire_called) { + mask |= (1ULL << s); + } + } + + // Remove from the mask queued buffers for which acquire has been called, since the consumer + // will not receive their buffer addresses and so must retain their cached information + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->acquire_called) { + mask &= ~(1ULL << current->slot); + } + ++current; + } + + LOG_DEBUG(Service_Nvnflinger, "returning mask {}", mask); + *out_slot_mask = mask; + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h new file mode 100644 index 000000000..50ed0bb5f --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/buffer_queue_defs.h" +#include "core/hle/service/nvnflinger/status.h" + +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + +namespace Service::android { + +class BufferItem; +class BufferQueueCore; +class IConsumerListener; + +class BufferQueueConsumer final { +public: + explicit BufferQueueConsumer(std::shared_ptr core_, + Service::Nvidia::NvCore::NvMap& nvmap_); + ~BufferQueueConsumer(); + + Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); + Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); + Status Connect(std::shared_ptr consumer_listener, bool controlled_by_app); + Status GetReleasedBuffers(u64* out_slot_mask); + +private: + std::shared_ptr core; + BufferQueueDefs::SlotsType& slots; + Service::Nvidia::NvCore::NvMap& nvmap; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp new file mode 100644 index 000000000..2dbe29616 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp + +#include "common/assert.h" + +#include "core/hle/service/nvnflinger/buffer_queue_core.h" + +namespace Service::android { + +BufferQueueCore::BufferQueueCore() = default; + +BufferQueueCore::~BufferQueueCore() = default; + +void BufferQueueCore::NotifyShutdown() { + std::scoped_lock lock{mutex}; + + is_shutting_down = true; + + SignalDequeueCondition(); +} + +void BufferQueueCore::SignalDequeueCondition() { + dequeue_possible.store(true); + dequeue_condition.notify_all(); +} + +bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock& lk) { + if (is_shutting_down) { + return false; + } + + dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); + dequeue_possible.store(false); + + return true; +} + +s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { + // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. + if (!use_async_buffer) { + return max_acquired_buffer_count; + } + + if (dequeue_buffer_cannot_block || async) { + return max_acquired_buffer_count + 1; + } + + return max_acquired_buffer_count; +} + +s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { + return GetMinUndequeuedBufferCountLocked(async) + 1; +} + +s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { + const auto min_buffer_count = GetMinMaxBufferCountLocked(async); + auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count); + + if (override_max_buffer_count != 0) { + ASSERT(override_max_buffer_count >= min_buffer_count); + max_buffer_count = override_max_buffer_count; + } + + // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed + // need to have their slots preserved. + for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + const auto state = slots[slot].buffer_state; + if (state == BufferState::Queued || state == BufferState::Dequeued) { + max_buffer_count = slot + 1; + } + } + + return max_buffer_count; +} + +s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { + return static_cast(std::count_if(slots.begin(), slots.end(), + [](const auto& slot) { return slot.is_preallocated; })); +} + +void BufferQueueCore::FreeBufferLocked(s32 slot) { + LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); + + slots[slot].graphic_buffer.reset(); + + slots[slot].buffer_state = BufferState::Free; + slots[slot].frame_number = UINT32_MAX; + slots[slot].acquire_called = false; + slots[slot].fence = Fence::NoFence(); +} + +void BufferQueueCore::FreeAllBuffersLocked() { + buffer_has_been_queued = false; + + for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + FreeBufferLocked(slot); + } +} + +bool BufferQueueCore::StillTracking(const BufferItem& item) const { + const BufferSlot& slot = slots[item.slot]; + + return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer); +} + +void BufferQueueCore::WaitWhileAllocatingLocked() const { + while (is_allocating) { + is_allocating_condition.wait(mutex); + } +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h new file mode 100644 index 000000000..9164f08a0 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "core/hle/service/nvnflinger/buffer_item.h" +#include "core/hle/service/nvnflinger/buffer_queue_defs.h" +#include "core/hle/service/nvnflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/status.h" +#include "core/hle/service/nvnflinger/window.h" + +namespace Service::android { + +class IConsumerListener; +class IProducerListener; + +class BufferQueueCore final { + friend class BufferQueueProducer; + friend class BufferQueueConsumer; + +public: + static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; + + BufferQueueCore(); + ~BufferQueueCore(); + + void NotifyShutdown(); + +private: + void SignalDequeueCondition(); + bool WaitForDequeueCondition(std::unique_lock& lk); + + s32 GetMinUndequeuedBufferCountLocked(bool async) const; + s32 GetMinMaxBufferCountLocked(bool async) const; + s32 GetMaxBufferCountLocked(bool async) const; + s32 GetPreallocatedBufferCountLocked() const; + void FreeBufferLocked(s32 slot); + void FreeAllBuffersLocked(); + bool StillTracking(const BufferItem& item) const; + void WaitWhileAllocatingLocked() const; + +private: + mutable std::mutex mutex; + bool is_abandoned{}; + bool consumer_controlled_by_app{}; + std::shared_ptr consumer_listener; + u32 consumer_usage_bit{}; + NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi}; + std::shared_ptr connected_producer_listener; + BufferQueueDefs::SlotsType slots{}; + std::vector queue; + s32 override_max_buffer_count{}; + std::condition_variable dequeue_condition; + std::atomic dequeue_possible{}; + const bool use_async_buffer{}; // This is always disabled on HOS + bool dequeue_buffer_cannot_block{}; + PixelFormat default_buffer_format{PixelFormat::Rgba8888}; + u32 default_width{1}; + u32 default_height{1}; + s32 default_max_buffer_count{2}; + const s32 max_acquired_buffer_count{}; // This is always zero on HOS + bool buffer_has_been_queued{}; + u64 frame_counter{}; + u32 transform_hint{}; + bool is_allocating{}; + mutable std::condition_variable_any is_allocating_condition; + bool is_shutting_down{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_defs.h b/src/core/hle/service/nvnflinger/buffer_queue_defs.h new file mode 100644 index 000000000..6fd3156f4 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_defs.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/buffer_slot.h" + +namespace Service::android::BufferQueueDefs { + +// BufferQueue will keep track of at most this value of buffers. +constexpr s32 NUM_BUFFER_SLOTS = 64; + +using SlotsType = std::array; + +} // namespace Service::android::BufferQueueDefs diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp new file mode 100644 index 000000000..cd0a13094 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -0,0 +1,933 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp + +#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/nvdrv/core/nvmap.h" +#include "core/hle/service/nvnflinger/buffer_queue_core.h" +#include "core/hle/service/nvnflinger/buffer_queue_producer.h" +#include "core/hle/service/nvnflinger/consumer_listener.h" +#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 { + +BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), + nvmap(nvmap_) { + buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); +} + +BufferQueueProducer::~BufferQueueProducer() { + service_context.CloseEvent(buffer_wait_event); +} + +Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr* buf) { + LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_Nvnflinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_Nvnflinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } + + slots[slot].request_buffer_called = true; + *buf = slots[slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { + LOG_DEBUG(Service_Nvnflinger, "count = {}", buffer_count); + + std::shared_ptr listener; + { + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_Nvnflinger, "buffer_count {} too large (max {})", buffer_count, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } + + // There must be no dequeued buffers when changing the buffer count. + for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Dequeued) { + LOG_ERROR(Service_Nvnflinger, "buffer owned by producer"); + return Status::BadValue; + } + } + + if (buffer_count == 0) { + core->override_max_buffer_count = 0; + core->SignalDequeueCondition(); + return Status::NoError; + } + + const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false); + if (buffer_count < min_buffer_slots) { + LOG_ERROR(Service_Nvnflinger, "requested buffer count {} is less than minimum {}", + buffer_count, min_buffer_slots); + return Status::BadValue; + } + + // Here we are guaranteed that the producer doesn't have any dequeued buffers and will + // release all of its buffer references. + if (core->GetPreallocatedBufferCountLocked() <= 0) { + core->FreeAllBuffersLocked(); + } + + core->override_max_buffer_count = buffer_count; + core->SignalDequeueCondition(); + buffer_wait_event->Signal(); + listener = core->consumer_listener; + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return Status::NoError; +} + +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, + std::unique_lock& lk) const { + bool try_again = true; + + while (try_again) { + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_Nvnflinger, "async mode is invalid with buffer count override"); + return Status::BadValue; + } + } + + // Free up any buffers that are in slots beyond the max buffer count + for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + ASSERT(slots[s].buffer_state == BufferState::Free); + if (slots[s].graphic_buffer != nullptr) { + core->FreeBufferLocked(s); + *return_flags |= Status::ReleaseAllBuffers; + } + } + + // Look for a free buffer to give to the client + *found = BufferQueueCore::INVALID_BUFFER_SLOT; + s32 dequeued_count{}; + s32 acquired_count{}; + for (s32 s{}; s < max_buffer_count; ++s) { + switch (slots[s].buffer_state) { + case BufferState::Dequeued: + ++dequeued_count; + break; + case BufferState::Acquired: + ++acquired_count; + break; + case BufferState::Free: + // We return the oldest of the free buffers to avoid stalling the producer if + // possible, since the consumer may still have pending reads of in-flight buffers + if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || + slots[s].frame_number < slots[*found].frame_number) { + *found = s; + } + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they did not set a buffer + // count + if (!core->override_max_buffer_count && dequeued_count) { + LOG_ERROR(Service_Nvnflinger, + "can't dequeue multiple buffers without setting the buffer count"); + return Status::InvalidOperation; + } + + // See whether a buffer has been queued since the last SetBufferCount so we know whether to + // perform the min undequeued buffers check below + if (core->buffer_has_been_queued) { + // Make sure the producer is not trying to dequeue more buffers than allowed + const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); + const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async); + if (new_undequeued_count < min_undequeued_count) { + LOG_ERROR(Service_Nvnflinger, + "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})", + min_undequeued_count, dequeued_count, new_undequeued_count); + return Status::InvalidOperation; + } + } + + // If we disconnect and reconnect quickly, we can be in a state where our slots are empty + // but we have many buffers in the queue. This can cause us to run out of memory if we + // outrun the consumer. Wait here if it looks like we have too many buffers queued up. + const bool too_many_buffers = core->queue.size() > static_cast(max_buffer_count); + if (too_many_buffers) { + LOG_ERROR(Service_Nvnflinger, "queue size is {}, waiting", core->queue.size()); + } + + // If no buffer is found, or if the queue has too many buffers outstanding, wait for a + // buffer to be acquired or released, or for the max buffer count to change. + try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; + if (try_again) { + // Return an error if we're in non-blocking mode (producer and consumer are controlled + // by the application). + if (core->dequeue_buffer_cannot_block && + (acquired_count <= core->max_acquired_buffer_count)) { + return Status::WouldBlock; + } + + if (!core->WaitForDequeueCondition(lk)) { + // We are no longer running + return Status::NoError; + } + } + } + + return Status::NoError; +} + +Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage) { + LOG_DEBUG(Service_Nvnflinger, "async={} w={} h={} format={}, usage={}", + async ? "true" : "false", width, height, format, usage); + + if ((width != 0 && height == 0) || (width == 0 && height != 0)) { + LOG_ERROR(Service_Nvnflinger, "invalid size: w={} h={}", width, height); + return Status::BadValue; + } + + Status return_flags = Status::NoError; + bool attached_by_consumer = false; + { + std::unique_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (format == PixelFormat::NoFormat) { + format = core->default_buffer_format; + } + + // Enable the usage bits the consumer requested + usage |= core->consumer_usage_bit; + + s32 found{}; + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_Nvnflinger, "no available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + attached_by_consumer = slots[found].attached_by_consumer; + + const bool use_default_size = !width && !height; + if (use_default_size) { + width = core->default_width; + height = core->default_height; + } + + slots[found].buffer_state = BufferState::Dequeued; + + const std::shared_ptr& buffer(slots[found].graphic_buffer); + if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) || + (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) { + slots[found].acquire_called = false; + slots[found].graphic_buffer = nullptr; + slots[found].request_buffer_called = false; + slots[found].fence = Fence::NoFence(); + + return_flags |= Status::BufferNeedsReallocation; + } + + *out_fence = slots[found].fence; + slots[found].fence = Fence::NoFence(); + } + + if ((return_flags & Status::BufferNeedsReallocation) != Status::None) { + LOG_DEBUG(Service_Nvnflinger, "allocating a new buffer for slot {}", *out_slot); + + auto graphic_buffer = std::make_shared(width, height, format, usage); + if (graphic_buffer == nullptr) { + LOG_ERROR(Service_Nvnflinger, "creating GraphicBuffer failed"); + return Status::NoMemory; + } + + { + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + slots[*out_slot].frame_number = UINT32_MAX; + slots[*out_slot].graphic_buffer = graphic_buffer; + } + } + + if (attached_by_consumer) { + return_flags |= Status::BufferNeedsReallocation; + } + + LOG_DEBUG(Service_Nvnflinger, "returning slot={} frame={}, flags={}", *out_slot, + slots[*out_slot].frame_number, return_flags); + + return return_flags; +} + +Status BufferQueueProducer::DetachBuffer(s32 slot) { + LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_Nvnflinger, "slot {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_Nvnflinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_Nvnflinger, "buffer in slot {} has not been requested", slot); + return Status::BadValue; + } + + core->FreeBufferLocked(slot); + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr* out_buffer, + Fence* out_fence) { + if (out_buffer == nullptr) { + LOG_ERROR(Service_Nvnflinger, "out_buffer must not be nullptr"); + return Status::BadValue; + } else if (out_fence == nullptr) { + LOG_ERROR(Service_Nvnflinger, "out_fence must not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + // Find the oldest valid slot + int found = BufferQueueCore::INVALID_BUFFER_SLOT; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { + if (found == BufferQueueCore::INVALID_BUFFER_SLOT || + slots[s].frame_number < slots[found].frame_number) { + found = s; + } + } + } + + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + return Status::NoMemory; + } + + LOG_DEBUG(Service_Nvnflinger, "Detached slot {}", found); + + *out_buffer = slots[found].graphic_buffer; + *out_fence = slots[found].fence; + + core->FreeBufferLocked(found); + + return Status::NoError; +} + +Status BufferQueueProducer::AttachBuffer(s32* out_slot, + const std::shared_ptr& buffer) { + if (out_slot == nullptr) { + LOG_ERROR(Service_Nvnflinger, "out_slot must not be nullptr"); + return Status::BadValue; + } else if (buffer == nullptr) { + LOG_ERROR(Service_Nvnflinger, "Cannot attach nullptr buffer"); + return Status::BadValue; + } + + std::unique_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + Status return_flags = Status::NoError; + s32 found{}; + + const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_Nvnflinger, "No available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + LOG_DEBUG(Service_Nvnflinger, "Returning slot {} flags={}", *out_slot, return_flags); + + slots[*out_slot].graphic_buffer = buffer; + slots[*out_slot].buffer_state = BufferState::Dequeued; + slots[*out_slot].fence = Fence::NoFence(); + slots[*out_slot].request_buffer_called = true; + + return return_flags; +} + +Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, + QueueBufferOutput* output) { + s64 timestamp{}; + bool is_auto_timestamp{}; + Common::Rectangle crop; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform; + u32 sticky_transform_{}; + bool async{}; + s32 swap_interval{}; + Fence fence{}; + + input.Deflate(×tamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, + &sticky_transform_, &async, &swap_interval, &fence); + + switch (scaling_mode) { + case NativeWindowScalingMode::Freeze: + case NativeWindowScalingMode::ScaleToWindow: + case NativeWindowScalingMode::ScaleCrop: + case NativeWindowScalingMode::NoScaleCrop: + break; + default: + LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode); + return Status::BadValue; + } + + std::shared_ptr frame_available_listener; + std::shared_ptr frame_replaced_listener; + s32 callback_ticket{}; + BufferItem item; + + { + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_Nvnflinger, "async mode is invalid with " + "buffer count override"); + return Status::BadValue; + } + } + + if (slot < 0 || slot >= max_buffer_count) { + LOG_ERROR(Service_Nvnflinger, "slot index {} out of range [0, {})", slot, + max_buffer_count); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_Nvnflinger, + "slot {} is not owned by the producer " + "(state = {})", + slot, slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_Nvnflinger, + "slot {} was queued without requesting " + "a buffer", + slot); + return Status::BadValue; + } + + LOG_DEBUG(Service_Nvnflinger, + "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot, + core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(), + crop.Bottom(), transform, scaling_mode); + + const std::shared_ptr& graphic_buffer(slots[slot].graphic_buffer); + Common::Rectangle buffer_rect(graphic_buffer->Width(), graphic_buffer->Height()); + Common::Rectangle cropped_rect; + [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect); + + if (cropped_rect != crop) { + LOG_ERROR(Service_Nvnflinger, "crop rect is not contained within the buffer in slot {}", + slot); + return Status::BadValue; + } + + slots[slot].fence = fence; + slots[slot].buffer_state = BufferState::Queued; + ++core->frame_counter; + slots[slot].frame_number = core->frame_counter; + + item.acquire_called = slots[slot].acquire_called; + item.graphic_buffer = slots[slot].graphic_buffer; + item.crop = crop; + item.transform = transform & ~NativeWindowTransform::InverseDisplay; + item.transform_to_display_inverse = + (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None; + item.scaling_mode = static_cast(scaling_mode); + item.timestamp = timestamp; + item.is_auto_timestamp = is_auto_timestamp; + item.frame_number = core->frame_counter; + item.slot = slot; + item.fence = fence; + item.is_droppable = core->dequeue_buffer_cannot_block || async; + item.swap_interval = swap_interval; + + nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); + + sticky_transform = sticky_transform_; + + if (core->queue.empty()) { + // When the queue is empty, we can simply queue this buffer + core->queue.push_back(item); + frame_available_listener = core->consumer_listener; + } else { + // When the queue is not empty, we need to look at the front buffer + // state to see if we need to replace it + auto front(core->queue.begin()); + + if (front->is_droppable) { + // If the front queued buffer is still being tracked, we first + // mark it as freed + if (core->StillTracking(*front)) { + slots[front->slot].buffer_state = BufferState::Free; + // Reset the frame number of the freed buffer so that it is the first in line to + // be dequeued again + slots[front->slot].frame_number = 0; + } + // Overwrite the droppable buffer with the incoming one + *front = item; + frame_replaced_listener = core->consumer_listener; + } else { + core->queue.push_back(item); + frame_available_listener = core->consumer_listener; + } + } + + core->buffer_has_been_queued = true; + core->SignalDequeueCondition(); + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast(core->queue.size())); + + // Take a ticket for the callback functions + callback_ticket = next_callback_ticket++; + } + + // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the + // consumer shouldn't need it + item.graphic_buffer.reset(); + item.slot = BufferItem::INVALID_BUFFER_SLOT; + + // Call back without the main BufferQueue lock held, but with the callback lock held so we can + // ensure that callbacks occur in order + { + std::scoped_lock lock{callback_mutex}; + while (callback_ticket != current_callback_ticket) { + callback_condition.wait(callback_mutex); + } + + if (frame_available_listener != nullptr) { + frame_available_listener->OnFrameAvailable(item); + } else if (frame_replaced_listener != nullptr) { + frame_replaced_listener->OnFrameReplaced(item); + } + + ++current_callback_ticket; + callback_condition.notify_all(); + } + + return Status::NoError; +} + +void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { + LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_Nvnflinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_Nvnflinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return; + } + + slots[slot].buffer_state = BufferState::Free; + slots[slot].frame_number = 0; + slots[slot].fence = fence; + + core->SignalDequeueCondition(); + buffer_wait_event->Signal(); +} + +Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { + std::scoped_lock lock{core->mutex}; + + if (out_value == nullptr) { + LOG_ERROR(Service_Nvnflinger, "outValue was nullptr"); + return Status::BadValue; + } + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u32 value{}; + switch (what) { + case NativeWindow::Width: + value = core->default_width; + break; + case NativeWindow::Height: + value = core->default_height; + break; + case NativeWindow::Format: + value = static_cast(core->default_buffer_format); + break; + case NativeWindow::MinUndequeedBuffers: + value = core->GetMinUndequeuedBufferCountLocked(false); + break; + case NativeWindow::StickyTransform: + value = sticky_transform; + break; + case NativeWindow::ConsumerRunningBehind: + value = (core->queue.size() > 1); + break; + case NativeWindow::ConsumerUsageBits: + value = core->consumer_usage_bit; + break; + default: + ASSERT(false); + return Status::BadValue; + } + + LOG_DEBUG(Service_Nvnflinger, "what = {}, value = {}", what, value); + + *out_value = static_cast(value); + + return Status::NoError; +} + +Status BufferQueueProducer::Connect(const std::shared_ptr& listener, + NativeWindowApi api, bool producer_controlled_by_app, + QueueBufferOutput* output) { + std::scoped_lock lock{core->mutex}; + + LOG_DEBUG(Service_Nvnflinger, "api = {} producer_controlled_by_app = {}", api, + producer_controlled_by_app); + + if (core->is_abandoned) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (core->consumer_listener == nullptr) { + LOG_ERROR(Service_Nvnflinger, "BufferQueue has no consumer"); + return Status::NoInit; + } + + if (output == nullptr) { + LOG_ERROR(Service_Nvnflinger, "output was nullptr"); + return Status::BadValue; + } + + if (core->connected_api != NativeWindowApi::NoConnectedApi) { + LOG_ERROR(Service_Nvnflinger, "already connected (cur = {} req = {})", core->connected_api, + api); + return Status::BadValue; + } + + Status status = Status::NoError; + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + core->connected_api = api; + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast(core->queue.size())); + core->connected_producer_listener = listener; + break; + default: + LOG_ERROR(Service_Nvnflinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + + core->buffer_has_been_queued = false; + core->dequeue_buffer_cannot_block = + core->consumer_controlled_by_app && producer_controlled_by_app; + + return status; +} + +Status BufferQueueProducer::Disconnect(NativeWindowApi api) { + LOG_DEBUG(Service_Nvnflinger, "api = {}", api); + + Status status = Status::NoError; + std::shared_ptr listener; + + { + std::scoped_lock lock{core->mutex}; + + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + // Disconnecting after the surface has been abandoned is a no-op. + return Status::NoError; + } + + // HACK: We are not Android. Remove handle for items in queue, and clear queue. + // Allows synchronous destruction of nvmap handles. + for (auto& item : core->queue) { + nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); + } + core->queue.clear(); + + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + if (core->connected_api == api) { + core->FreeAllBuffersLocked(); + core->connected_producer_listener = nullptr; + core->connected_api = NativeWindowApi::NoConnectedApi; + core->SignalDequeueCondition(); + buffer_wait_event->Signal(); + listener = core->consumer_listener; + } else { + LOG_ERROR(Service_Nvnflinger, "still connected to another api (cur = {} req = {})", + core->connected_api, api); + status = Status::BadValue; + } + break; + default: + LOG_ERROR(Service_Nvnflinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return status; +} + +Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, + const std::shared_ptr& buffer) { + LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + slots[slot] = {}; + slots[slot].graphic_buffer = buffer; + slots[slot].frame_number = 0; + + // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for + // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. + if (buffer) { + slots[slot].is_preallocated = true; + + core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked(); + core->default_width = buffer->Width(); + core->default_height = buffer->Height(); + core->default_buffer_format = buffer->Format(); + } + + core->SignalDequeueCondition(); + buffer_wait_event->Signal(); + + return Status::NoError; +} + +void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u32 flags) { + Status status{Status::NoError}; + InputParcel parcel_in{ctx.ReadBuffer()}; + OutputParcel parcel_out{}; + + switch (code) { + case TransactionId::Connect: { + const auto enable_listener = parcel_in.Read(); + const auto api = parcel_in.Read(); + const auto producer_controlled_by_app = parcel_in.Read(); + + UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!"); + + std::shared_ptr listener; + QueueBufferOutput output{}; + + status = Connect(listener, api, producer_controlled_by_app, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::SetPreallocatedBuffer: { + const auto slot = parcel_in.Read(); + const auto buffer = parcel_in.ReadObject(); + + status = SetPreallocatedBuffer(slot, buffer); + break; + } + case TransactionId::DequeueBuffer: { + const auto is_async = parcel_in.Read(); + const auto width = parcel_in.Read(); + const auto height = parcel_in.Read(); + const auto pixel_format = parcel_in.Read(); + const auto usage = parcel_in.Read(); + + s32 slot{}; + Fence fence{}; + + status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage); + + parcel_out.Write(slot); + parcel_out.WriteObject(&fence); + break; + } + case TransactionId::RequestBuffer: { + const auto slot = parcel_in.Read(); + + std::shared_ptr buf; + + status = RequestBuffer(slot, &buf); + + parcel_out.WriteObject(buf); + break; + } + case TransactionId::QueueBuffer: { + const auto slot = parcel_in.Read(); + + QueueBufferInput input{parcel_in}; + QueueBufferOutput output; + + status = QueueBuffer(slot, input, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::Query: { + const auto what = parcel_in.Read(); + + s32 value{}; + + status = Query(what, &value); + + parcel_out.Write(value); + break; + } + case TransactionId::CancelBuffer: { + const auto slot = parcel_in.Read(); + const auto fence = parcel_in.ReadFlattened(); + + CancelBuffer(slot, fence); + break; + } + case TransactionId::Disconnect: { + const auto api = parcel_in.Read(); + + status = Disconnect(api); + break; + } + case TransactionId::DetachBuffer: { + const auto slot = parcel_in.Read(); + + status = DetachBuffer(slot); + break; + } + case TransactionId::SetBufferCount: { + const auto buffer_count = parcel_in.Read(); + + status = SetBufferCount(buffer_count); + break; + } + case TransactionId::GetBufferHistory: + LOG_WARNING(Service_Nvnflinger, "(STUBBED) called, transaction=GetBufferHistory"); + break; + default: + ASSERT_MSG(false, "Unimplemented TransactionId {}", code); + break; + } + + parcel_out.Write(status); + + ctx.WriteBuffer(parcel_out.Serialize()); +} + +Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { + return buffer_wait_event->GetReadableEvent(); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h new file mode 100644 index 000000000..d4201c104 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h + +#pragma once + +#include +#include +#include + +#include "common/common_funcs.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvnflinger/binder.h" +#include "core/hle/service/nvnflinger/buffer_queue_defs.h" +#include "core/hle/service/nvnflinger/buffer_slot.h" +#include "core/hle/service/nvnflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvnflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/status.h" +#include "core/hle/service/nvnflinger/window.h" + +namespace Kernel { +class KernelCore; +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} // namespace Service::KernelHelpers + +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + +namespace Service::android { + +class BufferQueueCore; +class IProducerListener; + +class BufferQueueProducer final : public IBinder { +public: + explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_); + ~BufferQueueProducer(); + + void Transact(HLERequestContext& ctx, android::TransactionId code, u32 flags) override; + + Kernel::KReadableEvent& GetNativeHandle() override; + +public: + Status RequestBuffer(s32 slot, std::shared_ptr* buf); + Status SetBufferCount(s32 buffer_count); + Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage); + Status DetachBuffer(s32 slot); + Status DetachNextBuffer(std::shared_ptr* out_buffer, Fence* out_fence); + Status AttachBuffer(s32* outSlot, const std::shared_ptr& buffer); + Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output); + void CancelBuffer(s32 slot, const Fence& fence); + Status Query(NativeWindow what, s32* out_value); + Status Connect(const std::shared_ptr& listener, NativeWindowApi api, + bool producer_controlled_by_app, QueueBufferOutput* output); + + Status Disconnect(NativeWindowApi api); + Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr& buffer); + +private: + BufferQueueProducer(const BufferQueueProducer&) = delete; + + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, + std::unique_lock& lk) const; + + Kernel::KEvent* buffer_wait_event{}; + Service::KernelHelpers::ServiceContext& service_context; + + std::shared_ptr core; + BufferQueueDefs::SlotsType& slots; + u32 sticky_transform{}; + std::mutex callback_mutex; + s32 next_callback_ticket{}; + s32 current_callback_ticket{}; + std::condition_variable_any callback_condition; + + Service::Nvidia::NvCore::NvMap& nvmap; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h new file mode 100644 index 000000000..d25bca049 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/ui/fence.h" + +namespace Service::android { + +class GraphicBuffer; + +enum class BufferState : u32 { + Free = 0, + Dequeued = 1, + Queued = 2, + Acquired = 3, +}; + +struct BufferSlot final { + constexpr BufferSlot() = default; + + std::shared_ptr graphic_buffer; + BufferState buffer_state{BufferState::Free}; + bool request_buffer_called{}; + u64 frame_number{}; + Fence fence; + bool acquire_called{}; + bool attached_by_consumer{}; + bool is_preallocated{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_transform_flags.h b/src/core/hle/service/nvnflinger/buffer_transform_flags.h new file mode 100644 index 000000000..67aa5dad6 --- /dev/null +++ b/src/core/hle/service/nvnflinger/buffer_transform_flags.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class BufferTransformFlags : u32 { + /// No transform flags are set + Unset = 0x00, + /// Flip source image horizontally (around the vertical axis) + FlipH = 0x01, + /// Flip source image vertically (around the horizontal axis) + FlipV = 0x02, + /// Rotate source image 90 degrees clockwise + Rotate90 = 0x04, + /// Rotate source image 180 degrees + Rotate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Rotate270 = 0x07, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp new file mode 100644 index 000000000..4dcda8dac --- /dev/null +++ b/src/core/hle/service/nvnflinger/consumer_base.cpp @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.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/consumer_base.h" +#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" + +namespace Service::android { + +ConsumerBase::ConsumerBase(std::unique_ptr consumer_) + : consumer{std::move(consumer_)} {} + +ConsumerBase::~ConsumerBase() { + std::scoped_lock lock{mutex}; + + ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); +} + +void ConsumerBase::Connect(bool controlled_by_app) { + consumer->Connect(shared_from_this(), controlled_by_app); +} + +void ConsumerBase::FreeBufferLocked(s32 slot_index) { + LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); + + slots[slot_index].graphic_buffer = nullptr; + slots[slot_index].fence = Fence::NoFence(); + slots[slot_index].frame_number = 0; +} + +void ConsumerBase::OnFrameAvailable(const BufferItem& item) { + LOG_DEBUG(Service_Nvnflinger, "called"); +} + +void ConsumerBase::OnFrameReplaced(const BufferItem& item) { + LOG_DEBUG(Service_Nvnflinger, "called"); +} + +void ConsumerBase::OnBuffersReleased() { + std::scoped_lock lock{mutex}; + + LOG_DEBUG(Service_Nvnflinger, "called"); + + if (is_abandoned) { + // Nothing to do if we're already abandoned. + return; + } + + u64 mask = 0; + consumer->GetReleasedBuffers(&mask); + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + if (mask & (1ULL << i)) { + FreeBufferLocked(i); + } + } +} + +void ConsumerBase::OnSidebandStreamChanged() {} + +Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) { + Status err = consumer->AcquireBuffer(item, present_when); + if (err != Status::NoError) { + return err; + } + + if (item->graphic_buffer != nullptr) { + slots[item->slot].graphic_buffer = item->graphic_buffer; + } + + slots[item->slot].frame_number = item->frame_number; + slots[item->slot].fence = item->fence; + + LOG_DEBUG(Service_Nvnflinger, "slot={}", item->slot); + + return Status::NoError; +} + +Status ConsumerBase::AddReleaseFenceLocked(s32 slot, + const std::shared_ptr& graphic_buffer, + const Fence& fence) { + LOG_DEBUG(Service_Nvnflinger, "slot={}", slot); + + // If consumer no longer tracks this graphic_buffer, we can safely + // drop this fence, as it will never be received by the producer. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + slots[slot].fence = fence; + + return Status::NoError; +} + +Status ConsumerBase::ReleaseBufferLocked(s32 slot, + const std::shared_ptr& graphic_buffer) { + // If consumer no longer tracks this graphic_buffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + LOG_DEBUG(Service_Nvnflinger, "slot={}", slot); + Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence); + if (err == Status::StaleBufferSlot) { + FreeBufferLocked(slot); + } + + slots[slot].fence = Fence::NoFence(); + + return err; +} + +bool ConsumerBase::StillTracking(s32 slot, + const std::shared_ptr& graphic_buffer) const { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return false; + } + + return (slots[slot].graphic_buffer != nullptr && + slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle()); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h new file mode 100644 index 000000000..264829414 --- /dev/null +++ b/src/core/hle/service/nvnflinger/consumer_base.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h + +#pragma once + +#include +#include +#include +#include + +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/buffer_queue_defs.h" +#include "core/hle/service/nvnflinger/consumer_listener.h" +#include "core/hle/service/nvnflinger/status.h" + +namespace Service::android { + +class BufferItem; +class BufferQueueConsumer; + +class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this { +public: + void Connect(bool controlled_by_app); + +protected: + explicit ConsumerBase(std::unique_ptr consumer_); + ~ConsumerBase() override; + + void OnFrameAvailable(const BufferItem& item) override; + void OnFrameReplaced(const BufferItem& item) override; + void OnBuffersReleased() override; + void OnSidebandStreamChanged() override; + + void FreeBufferLocked(s32 slot_index); + Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); + Status ReleaseBufferLocked(s32 slot, const std::shared_ptr& graphic_buffer); + bool StillTracking(s32 slot, const std::shared_ptr& graphic_buffer) const; + Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr& graphic_buffer, + const Fence& fence); + + struct Slot final { + std::shared_ptr graphic_buffer; + Fence fence; + u64 frame_number{}; + }; + +protected: + std::array slots; + + bool is_abandoned{}; + + std::unique_ptr consumer; + + mutable std::mutex mutex; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/consumer_listener.h b/src/core/hle/service/nvnflinger/consumer_listener.h new file mode 100644 index 000000000..74a193988 --- /dev/null +++ b/src/core/hle/service/nvnflinger/consumer_listener.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h + +#pragma once + +namespace Service::android { + +class BufferItem; + +/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events +/// that the consumer may wish to react to. +class IConsumerListener { +public: + IConsumerListener() = default; + virtual ~IConsumerListener() = default; + + virtual void OnFrameAvailable(const BufferItem& item) = 0; + virtual void OnFrameReplaced(const BufferItem& item) = 0; + virtual void OnBuffersReleased() = 0; + virtual void OnSidebandStreamChanged() = 0; +}; + +}; // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp new file mode 100644 index 000000000..d72b49a8e --- /dev/null +++ b/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp + +#include "core/hle/service/nvnflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvnflinger/parcel.h" + +namespace Service::android { + +QueueBufferInput::QueueBufferInput(InputParcel& parcel) { + parcel.ReadFlattened(*this); +} + +QueueBufferOutput::QueueBufferOutput() = default; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h new file mode 100644 index 000000000..21d7b31f3 --- /dev/null +++ b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvnflinger/ui/fence.h" +#include "core/hle/service/nvnflinger/window.h" + +namespace Service::android { + +class InputParcel; + +#pragma pack(push, 1) +struct QueueBufferInput final { + explicit QueueBufferInput(InputParcel& parcel); + + void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle* crop_, + NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, + u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const { + *timestamp_ = timestamp; + *is_auto_timestamp_ = static_cast(is_auto_timestamp); + *crop_ = crop; + *scaling_mode_ = scaling_mode; + *transform_ = transform; + *sticky_transform_ = sticky_transform; + *async_ = static_cast(async); + *swap_interval_ = swap_interval; + *fence_ = fence; + } + +private: + s64 timestamp{}; + s32 is_auto_timestamp{}; + Common::Rectangle crop{}; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform{}; + u32 sticky_transform{}; + s32 async{}; + s32 swap_interval{}; + Fence fence{}; +}; +#pragma pack(pop) +static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size"); + +struct QueueBufferOutput final { + QueueBufferOutput(); + + void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const { + *width_ = width; + *height_ = height; + *transform_hint_ = transform_hint; + *num_pending_buffers_ = num_pending_buffers; + } + + void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) { + width = width_; + height = height_; + transform_hint = transform_hint_; + num_pending_buffers = num_pending_buffers_; + } + +private: + u32 width{}; + u32 height{}; + u32 transform_hint{}; + u32 num_pending_buffers{}; +}; +static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp new file mode 100644 index 000000000..b86a79ec9 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" + +namespace Service::Nvnflinger { + +HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) + : service_context(system_, "HosBinderDriverServer") {} + +HosBinderDriverServer::~HosBinderDriverServer() {} + +u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr&& binder) { + std::scoped_lock lk{lock}; + + last_id++; + + producers[last_id] = std::move(binder); + + return last_id; +} + +android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { + std::scoped_lock lk{lock}; + + if (auto search = producers.find(id); search != producers.end()) { + return search->second.get(); + } + + return {}; +} + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h new file mode 100644 index 000000000..58bb9469a --- /dev/null +++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvnflinger/binder.h" + +namespace Core { +class System; +} + +namespace Service::Nvnflinger { + +class HosBinderDriverServer final { +public: + explicit HosBinderDriverServer(Core::System& system_); + ~HosBinderDriverServer(); + + u64 RegisterProducer(std::unique_ptr&& binder); + + android::IBinder* TryGetProducer(u64 id); + +private: + KernelHelpers::ServiceContext service_context; + + std::unordered_map> producers; + std::mutex lock; + u64 last_id{}; +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp new file mode 100644 index 000000000..4988e6e17 --- /dev/null +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -0,0 +1,335 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/microprofile.h" +#include "common/scope_exit.h" +#include "common/settings.h" +#include "common/thread.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvnflinger/buffer_item_consumer.h" +#include "core/hle/service/nvnflinger/buffer_queue_core.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" +#include "core/hle/service/nvnflinger/nvnflinger.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" +#include "core/hle/service/vi/vi_results.h" +#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.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(false); + vsync_signal.store(false); + + guard->lock(); + + Compose(); + + guard->unlock(); + } +} + +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(); + + // Schedule the screen composition events + multi_composition_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional { + vsync_signal.store(true); + vsync_signal.notify_all(); + return std::chrono::nanoseconds(GetNextTicks()); + }); + + single_composition_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional { + 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.store(true); + vsync_signal.notify_all(); + } else { + system.CoreTiming().UnscheduleEvent(single_composition_event, {}); + } + + ShutdownLayers(); + + if (nvdrv) { + nvdrv->Close(disp_fd); + } +} + +void Nvnflinger::ShutdownLayers() { + for (auto& display : displays) { + for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { + display.GetLayer(layer).Core().NotifyShutdown(); + } + } +} + +void Nvnflinger::SetNVDrvInstance(std::shared_ptr instance) { + nvdrv = std::move(instance); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); +} + +std::optional 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 Nvnflinger::CreateLayer(u64 display_id) { + 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); + return layer_id; +} + +void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { + const auto buffer_id = next_buffer_queue_id++; + display.CreateLayer(layer_id, buffer_id, nvdrv->container); +} + +void Nvnflinger::CloseLayer(u64 layer_id) { + const auto lock_guard = Lock(); + + for (auto& display : displays) { + display.CloseLayer(layer_id); + } +} + +std::optional Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) { + const auto lock_guard = Lock(); + const auto* const layer = FindOrCreateLayer(display_id, layer_id); + + if (layer == nullptr) { + return std::nullopt; + } + + return layer->GetBinderId(); +} + +ResultVal Nvnflinger::FindVsyncEvent(u64 display_id) { + const auto lock_guard = Lock(); + auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return VI::ResultNotFound; + } + + return display->GetVSyncEvent(); +} + +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); +} + +const VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) const { + const auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return nullptr; + } + + return display->FindLayer(layer_id); +} + +VI::Layer* Nvnflinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { + auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return nullptr; + } + + auto* layer = display->FindLayer(layer_id); + + if (layer == nullptr) { + LOG_DEBUG(Service_Nvnflinger, "Layer at id {} not found. Trying to create it.", layer_id); + CreateLayerAtId(*display, layer_id); + return display->FindLayer(layer_id); + } + + return layer; +} + +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; + + // TODO(Subv): Support more than 1 layer. + VI::Layer& layer = display.GetLayer(0); + + android::BufferItem buffer{}; + const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); + + if (status != android::Status::NoError) { + continue; + } + + const auto& igbp_buffer = *buffer.graphic_buffer; + + if (!system.IsPoweredOn()) { + return; // We are likely shutting down + } + + // Now send the buffer to the GPU for drawing. + // TODO(Subv): Support more than just disp0. The display device selection is probably based + // on which display we're drawing (Default, Internal, External, etc) + auto nvdisp = nvdrv->GetDevice(disp_fd); + ASSERT(nvdisp); + + guard->unlock(); + Common::Rectangle crop_rect{ + static_cast(buffer.crop.Left()), static_cast(buffer.crop.Top()), + static_cast(buffer.crop.Right()), static_cast(buffer.crop.Bottom())}; + + nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), + igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), + static_cast(buffer.transform), crop_rect, + buffer.fence.fences, buffer.fence.num_fences); + + MicroProfileFlip(); + guard->lock(); + + swap_interval = buffer.swap_interval; + + layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); + } +} + +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; + } + } + + // As an extension, treat nonpositive swap interval as framerate multiplier. + const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast(1 - swap_interval) + : 60.f / static_cast(swap_interval); + + return static_cast(speed_scale * (1000000000.f / effective_fps)); +} + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h new file mode 100644 index 000000000..a043cceb2 --- /dev/null +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -0,0 +1,155 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/common_types.h" +#include "common/polyfill_thread.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.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 Layer; +} // namespace Service::VI + +namespace Service::android { +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android + +namespace Service::Nvnflinger { + +class Nvnflinger final { +public: + explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); + ~Nvnflinger(); + + void ShutdownLayers(); + + /// Sets the NVDrv module instance to use to send buffers to the GPU. + void SetNVDrvInstance(std::shared_ptr instance); + + /// Opens the specified display and returns the ID. + /// + /// If an invalid display name is provided, then an empty optional is returned. + [[nodiscard]] std::optional 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 CreateLayer(u64 display_id); + + /// Closes a layer on all displays for the given layer ID. + void CloseLayer(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 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]] ResultVal FindVsyncEvent(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: + struct Layer { + std::unique_ptr core; + std::unique_ptr producer; + }; + +private: + [[nodiscard]] std::unique_lock 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); + + /// Finds the layer identified by the specified ID in the desired display. + [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; + + /// Finds the layer identified by the specified ID in the desired display, + /// or creates the layer if it is not found. + /// To be used when the system expects the specified ID to already exist. + [[nodiscard]] VI::Layer* FindOrCreateLayer(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); + + void SplitVSync(std::stop_token stop_token); + + std::shared_ptr nvdrv; + s32 disp_fd; + + std::list 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; + + /// Event that handles screen composition. + std::shared_ptr multi_composition_event; + std::shared_ptr single_composition_event; + + std::shared_ptr guard; + + Core::System& system; + + std::atomic vsync_signal; + + std::jthread vsync_thread; + + KernelHelpers::ServiceContext service_context; + + HosBinderDriverServer& hos_binder_driver_server; +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h new file mode 100644 index 000000000..d1b6201e0 --- /dev/null +++ b/src/core/hle/service/nvnflinger/parcel.h @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" + +namespace Service::android { + +struct ParcelHeader { + u32 data_size; + u32 data_offset; + u32 objects_size; + u32 objects_offset; +}; +static_assert(sizeof(ParcelHeader) == 16, "ParcelHeader has wrong size"); + +class InputParcel final { +public: + explicit InputParcel(std::span in_data) : read_buffer(std::move(in_data)) { + DeserializeHeader(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); + } + + template + void Read(T& val) { + static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= read_buffer.size()); + + std::memcpy(&val, read_buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + read_index = Common::AlignUp(read_index, 4); + } + + template + T Read() { + T val; + Read(val); + return val; + } + + template + void ReadFlattened(T& val) { + const auto flattened_size = Read(); + ASSERT(sizeof(T) == flattened_size); + Read(val); + } + + template + T ReadFlattened() { + T val; + ReadFlattened(val); + return val; + } + + template + T ReadUnaligned() { + static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= read_buffer.size()); + + T val; + std::memcpy(&val, read_buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + return val; + } + + template + const std::shared_ptr ReadObject() { + static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); + + const auto is_valid{Read()}; + + if (is_valid) { + auto result = std::make_shared(); + ReadFlattened(*result); + return result; + } + + return {}; + } + + std::u16string ReadInterfaceToken() { + [[maybe_unused]] const u32 unknown = Read(); + const u32 length = Read(); + + std::u16string token; + token.reserve(length + 1); + + for (u32 ch = 0; ch < length + 1; ++ch) { + token.push_back(ReadUnaligned()); + } + + read_index = Common::AlignUp(read_index, 4); + + return token; + } + + void DeserializeHeader() { + ASSERT(read_buffer.size() > sizeof(ParcelHeader)); + + ParcelHeader header{}; + std::memcpy(&header, read_buffer.data(), sizeof(ParcelHeader)); + + read_index = header.data_offset; + } + +private: + std::span read_buffer; + std::size_t read_index = 0; +}; + +class OutputParcel final { +public: + static constexpr std::size_t DefaultBufferSize = 0x40; + + OutputParcel() : buffer(DefaultBufferSize) {} + + template + explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) { + Write(out_data); + } + + template + void Write(const T& val) { + static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); + + if (buffer.size() < write_index + sizeof(T)) { + buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); + } + + std::memcpy(buffer.data() + write_index, &val, sizeof(T)); + write_index += sizeof(T); + write_index = Common::AlignUp(write_index, 4); + } + + template + void WriteObject(const T* ptr) { + static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); + + if (!ptr) { + Write(0); + return; + } + + Write(1); + Write(sizeof(T)); + Write(*ptr); + } + + template + void WriteObject(const std::shared_ptr ptr) { + WriteObject(ptr.get()); + } + + std::vector Serialize() const { + ParcelHeader header{}; + header.data_size = static_cast(write_index - sizeof(ParcelHeader)); + header.data_offset = sizeof(ParcelHeader); + header.objects_size = 4; + header.objects_offset = static_cast(sizeof(ParcelHeader) + header.data_size); + std::memcpy(buffer.data(), &header, sizeof(ParcelHeader)); + + return buffer; + } + +private: + mutable std::vector buffer; + std::size_t write_index = sizeof(ParcelHeader); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/pixel_format.h b/src/core/hle/service/nvnflinger/pixel_format.h new file mode 100644 index 000000000..f77d0acfb --- /dev/null +++ b/src/core/hle/service/nvnflinger/pixel_format.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class PixelFormat : u32 { + NoFormat = 0, + Rgba8888 = 1, + Rgbx8888 = 2, + Rgb888 = 3, + Rgb565 = 4, + Bgra8888 = 5, + Rgba5551 = 6, + Rgba4444 = 7, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/producer_listener.h b/src/core/hle/service/nvnflinger/producer_listener.h new file mode 100644 index 000000000..6bf8aaf1e --- /dev/null +++ b/src/core/hle/service/nvnflinger/producer_listener.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h + +#pragma once + +namespace Service::android { + +class IProducerListener { +public: + virtual ~IProducerListener() = default; + virtual void OnBufferReleased() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h new file mode 100644 index 000000000..7af166c40 --- /dev/null +++ b/src/core/hle/service/nvnflinger/status.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +enum class Status : s32 { + None = 0, + NoError = 0, + StaleBufferSlot = 1, + NoBufferAvailable = 2, + PresentLater = 3, + WouldBlock = -11, + NoMemory = -12, + Busy = -16, + NoInit = -19, + BadValue = -22, + InvalidOperation = -37, + BufferNeedsReallocation = 1, + ReleaseAllBuffers = 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(Status); + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/ui/fence.h b/src/core/hle/service/nvnflinger/ui/fence.h new file mode 100644 index 000000000..536e8156d --- /dev/null +++ b/src/core/hle/service/nvnflinger/ui/fence.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Service::android { + +class Fence { +public: + constexpr Fence() = default; + + static constexpr Fence NoFence() { + Fence fence; + fence.fences[0].id = -1; + return fence; + } + +public: + u32 num_fences{}; + std::array fences{}; +}; +static_assert(sizeof(Fence) == 36, "Fence has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h new file mode 100644 index 000000000..75d1705a8 --- /dev/null +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/nvnflinger/pixel_format.h" + +namespace Service::android { + +class GraphicBuffer final { +public: + constexpr GraphicBuffer() = default; + + constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) + : width{static_cast(width_)}, height{static_cast(height_)}, format{format_}, + usage{static_cast(usage_)} {} + + constexpr u32 Width() const { + return static_cast(width); + } + + constexpr u32 Height() const { + return static_cast(height); + } + + constexpr u32 Stride() const { + return static_cast(stride); + } + + constexpr u32 Usage() const { + return static_cast(usage); + } + + constexpr PixelFormat Format() const { + return format; + } + + constexpr u32 BufferId() const { + return buffer_id; + } + + constexpr PixelFormat ExternalFormat() const { + return external_format; + } + + constexpr u32 Handle() const { + return handle; + } + + constexpr u32 Offset() const { + return offset; + } + + constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_, + u32 usage_) const { + if (static_cast(width_) != width) { + return true; + } + + if (static_cast(height_) != height) { + return true; + } + + if (format_ != format) { + return true; + } + + if ((static_cast(usage) & usage_) != usage_) { + return true; + } + + return false; + } + +private: + u32 magic{}; + s32 width{}; + s32 height{}; + s32 stride{}; + PixelFormat format{}; + s32 usage{}; + INSERT_PADDING_WORDS(1); + u32 index{}; + INSERT_PADDING_WORDS(3); + u32 buffer_id{}; + INSERT_PADDING_WORDS(6); + PixelFormat external_format{}; + INSERT_PADDING_WORDS(10); + u32 handle{}; + u32 offset{}; + INSERT_PADDING_WORDS(60); +}; +static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/window.h b/src/core/hle/service/nvnflinger/window.h new file mode 100644 index 000000000..61cca5b01 --- /dev/null +++ b/src/core/hle/service/nvnflinger/window.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +/// Attributes queryable with Query +enum class NativeWindow : s32 { + Width = 0, + Height = 1, + Format = 2, + MinUndequeedBuffers = 3, + QueuesToWindowComposer = 4, + ConcreteType = 5, + DefaultWidth = 6, + DefaultHeight = 7, + TransformHint = 8, + ConsumerRunningBehind = 9, + ConsumerUsageBits = 10, + StickyTransform = 11, + DefaultDataSpace = 12, + BufferAge = 13, +}; + +/// Parameter for Connect/Disconnect +enum class NativeWindowApi : s32 { + NoConnectedApi = 0, + Egl = 1, + Cpu = 2, + Media = 3, + Camera = 4, +}; + +/// Scaling mode parameter for QueueBuffer +enum class NativeWindowScalingMode : s32 { + Freeze = 0, + ScaleToWindow = 1, + ScaleCrop = 2, + NoScaleCrop = 3, +}; + +/// Transform parameter for QueueBuffer +enum class NativeWindowTransform : u32 { + None = 0x0, + InverseDisplay = 0x08, +}; +DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform); + +} // namespace Service::android diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 6415fc310..eed615377 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -49,8 +49,8 @@ #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/nvflinger/hos_binder_driver_server.h" -#include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" #include "core/hle/service/pctl/pctl_module.h" @@ -210,12 +210,12 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, /// Initialize Services Services::Services(std::shared_ptr& sm, Core::System& system) - : hos_binder_driver_server{std::make_unique(system)}, - nv_flinger{std::make_unique(system, *hos_binder_driver_server)} { + : hos_binder_driver_server{std::make_unique(system)}, + nv_flinger{std::make_unique(system, *hos_binder_driver_server)} { auto& kernel = system.Kernel(); - // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it + // Nvnflinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 06226409a..0f79a1b7e 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -28,10 +28,10 @@ namespace FileSystem { class FileSystemController; } -namespace NVFlinger { +namespace Nvnflinger { class HosBinderDriverServer; -class NVFlinger; -} // namespace NVFlinger +class Nvnflinger; +} // namespace Nvnflinger namespace SM { class ServiceManager; @@ -228,8 +228,8 @@ public: void KillNVNFlinger(); private: - std::unique_ptr hos_binder_driver_server; - std::unique_ptr nv_flinger; + std::unique_ptr hos_binder_driver_server; + std::unique_ptr nv_flinger; }; } // namespace Service diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 8ef74f1f0..69af2868a 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -12,11 +12,11 @@ #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/nvflinger/buffer_item_consumer.h" -#include "core/hle/service/nvflinger/buffer_queue_consumer.h" -#include "core/hle/service/nvflinger/buffer_queue_core.h" -#include "core/hle/service/nvflinger/buffer_queue_producer.h" -#include "core/hle/service/nvflinger/hos_binder_driver_server.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/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" @@ -39,7 +39,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont } Display::Display(u64 id, std::string name_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + 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_} { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 0b65a65da..3f31d1f32 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -23,7 +23,7 @@ namespace Service::KernelHelpers { class ServiceContext; } -namespace Service::NVFlinger { +namespace Service::Nvnflinger { class HosBinderDriverServer; } @@ -45,12 +45,12 @@ public: /// Constructs a display with a given unique ID and name. /// /// @param id The unique ID for this display. - /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance. + /// @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_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + Display(u64 id, std::string name_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_, KernelHelpers::ServiceContext& service_context_, Core::System& system_); ~Display(); @@ -133,7 +133,7 @@ public: private: u64 display_id; std::string name; - NVFlinger::HosBinderDriverServer& hos_binder_driver_server; + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; KernelHelpers::ServiceContext& service_context; std::vector> layers; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index fca076d7a..68eab5133 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -21,11 +21,11 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/binder.h" -#include "core/hle/service/nvflinger/buffer_queue_producer.h" -#include "core/hle/service/nvflinger/hos_binder_driver_server.h" -#include "core/hle/service/nvflinger/nvflinger.h" -#include "core/hle/service/nvflinger/parcel.h" +#include "core/hle/service/nvnflinger/binder.h" +#include "core/hle/service/nvnflinger/buffer_queue_producer.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" +#include "core/hle/service/nvnflinger/parcel.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" @@ -73,7 +73,7 @@ static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); class IHOSBinderDriver final : public ServiceFramework { public: - explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_) + explicit IHOSBinderDriver(Core::System& system_, Nvnflinger::HosBinderDriverServer& server_) : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, @@ -126,7 +126,7 @@ private: } private: - NVFlinger::HosBinderDriverServer& server; + Nvnflinger::HosBinderDriverServer& server; }; class ISystemDisplayService final : public ServiceFramework { @@ -232,7 +232,7 @@ private: class IManagerDisplayService final : public ServiceFramework { public: - explicit IManagerDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) + explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_) : ServiceFramework{system_, "IManagerDisplayService"}, nv_flinger{nv_flinger_} { // clang-format off static const FunctionInfo functions[] = { @@ -383,13 +383,13 @@ private: rb.Push(ResultSuccess); } - NVFlinger::NVFlinger& nv_flinger; + Nvnflinger::Nvnflinger& nv_flinger; }; class IApplicationDisplayService final : public ServiceFramework { public: - IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{hos_binder_driver_server_} { @@ -774,8 +774,8 @@ private: } } - NVFlinger::NVFlinger& nv_flinger; - NVFlinger::HosBinderDriverServer& hos_binder_driver_server; + Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; }; static bool IsValidServiceAccess(Permission permission, Policy policy) { @@ -791,8 +791,8 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { } void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Nvnflinger::Nvnflinger& nv_flinger, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, Permission permission) { IPC::RequestParser rp{ctx}; const auto policy = rp.PopEnum(); @@ -809,8 +809,8 @@ void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, rb.PushIpcInterface(system, nv_flinger, hos_binder_driver_server); } -void LoopProcess(Core::System& system, NVFlinger::NVFlinger& nv_flinger, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server) { +void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) { auto server_manager = std::make_unique(system); server_manager->RegisterNamedService( diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 48b2f30aa..a35b62f97 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -13,10 +13,10 @@ namespace Service { class HLERequestContext; } -namespace Service::NVFlinger { +namespace Service::Nvnflinger { class HosBinderDriverServer; -class NVFlinger; -} // namespace Service::NVFlinger +class Nvnflinger; +} // namespace Service::Nvnflinger namespace Service::VI { @@ -43,12 +43,12 @@ enum class Policy { namespace detail { void GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Nvnflinger::Nvnflinger& nv_flinger, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, Permission permission); } // namespace detail -void LoopProcess(Core::System& system, NVFlinger::NVFlinger& nv_flinger, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server); +void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server); } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 019e55811..0f06dc2f3 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -7,8 +7,8 @@ namespace Service::VI { -VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) +VI_M::VI_M(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ hos_binder_driver_server_} { static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 392da04a3..9ca6f3905 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -9,24 +9,24 @@ namespace Core { class System; } -namespace Service::NVFlinger { +namespace Service::Nvnflinger { class HosBinderDriverServer; -class NVFlinger; -} // namespace Service::NVFlinger +class Nvnflinger; +} // namespace Service::Nvnflinger namespace Service::VI { class VI_M final : public ServiceFramework { public: - explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); + explicit VI_M(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_M() override; private: void GetDisplayService(HLERequestContext& ctx); - NVFlinger::NVFlinger& nv_flinger; - NVFlinger::HosBinderDriverServer& hos_binder_driver_server; + Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 901c5988e..77f7a88ff 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -7,8 +7,8 @@ namespace Service::VI { -VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) +VI_S::VI_S(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ hos_binder_driver_server_} { static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 34282fcfd..157839c91 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -9,24 +9,24 @@ namespace Core { class System; } -namespace Service::NVFlinger { +namespace Service::Nvnflinger { class HosBinderDriverServer; -class NVFlinger; -} // namespace Service::NVFlinger +class Nvnflinger; +} // namespace Service::Nvnflinger namespace Service::VI { class VI_S final : public ServiceFramework { public: - explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); + explicit VI_S(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_S() override; private: void GetDisplayService(HLERequestContext& ctx); - NVFlinger::NVFlinger& nv_flinger; - NVFlinger::HosBinderDriverServer& hos_binder_driver_server; + Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index 08c5cf486..59e13c86b 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -7,8 +7,8 @@ namespace Service::VI { -VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) +VI_U::VI_U(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ hos_binder_driver_server_} { static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index 8b0a8dd02..5d9ca54c6 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -9,24 +9,24 @@ namespace Core { class System; } -namespace Service::NVFlinger { +namespace Service::Nvnflinger { class HosBinderDriverServer; -class NVFlinger; -} // namespace Service::NVFlinger +class Nvnflinger; +} // namespace Service::Nvnflinger namespace Service::VI { class VI_U final : public ServiceFramework { public: - explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, - NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); + explicit VI_U(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_U() override; private: void GetDisplayService(HLERequestContext& ctx); - NVFlinger::NVFlinger& nv_flinger; - NVFlinger::HosBinderDriverServer& hos_binder_driver_server; + Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index d93f5a37f..5f3bffcab 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -5,8 +5,8 @@ #include "common/common_types.h" #include "common/math_util.h" -#include "core/hle/service/nvflinger/buffer_transform_flags.h" -#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/buffer_transform_flags.h" +#include "core/hle/service/nvnflinger/pixel_format.h" namespace Tegra { -- cgit v1.2.3