service: vi: Retrieve vsync event once per display
The display vsync event can only be retrieved once per display. Returns VI::ResultPermissionDenied if we attempt to retrieve the vsync event for the same display. Prevents games such as .hack//G.U. Last Recode from consuming all the handles in the handle table by spamming vsync event retrievals and allows it to go in game.
This commit is contained in:
		| @@ -22,6 +22,7 @@ | ||||
| #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" | ||||
|  | ||||
| namespace Service::NVFlinger { | ||||
| @@ -163,15 +164,15 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { | ||||
|     return layer->GetBinderId(); | ||||
| } | ||||
|  | ||||
| Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { | ||||
| ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) { | ||||
|     const auto lock_guard = Lock(); | ||||
|     auto* const display = FindDisplay(display_id); | ||||
|  | ||||
|     if (display == nullptr) { | ||||
|         return nullptr; | ||||
|         return VI::ResultNotFound; | ||||
|     } | ||||
|  | ||||
|     return &display->GetVSyncEvent(); | ||||
|     return display->GetVSyncEvent(); | ||||
| } | ||||
|  | ||||
| VI::Display* NVFlinger::FindDisplay(u64 display_id) { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
|  | ||||
| namespace Common { | ||||
| @@ -71,8 +72,9 @@ public: | ||||
|  | ||||
|     /// Gets the vsync event for the specified display. | ||||
|     /// | ||||
|     /// If an invalid display ID is provided, then nullptr is returned. | ||||
|     [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); | ||||
|     /// 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<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id); | ||||
|  | ||||
|     /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | ||||
|     /// finished. | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
| #include "core/hle/service/nvflinger/hos_binder_driver_server.h" | ||||
| #include "core/hle/service/vi/display/vi_display.h" | ||||
| #include "core/hle/service/vi/layer/vi_layer.h" | ||||
| #include "core/hle/service/vi/vi_results.h" | ||||
|  | ||||
| namespace Service::VI { | ||||
|  | ||||
| @@ -55,8 +56,18 @@ const Layer& Display::GetLayer(std::size_t index) const { | ||||
|     return *layers.at(index); | ||||
| } | ||||
|  | ||||
| Kernel::KReadableEvent& Display::GetVSyncEvent() { | ||||
|     return vsync_event->GetReadableEvent(); | ||||
| ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { | ||||
|     if (got_vsync_event) { | ||||
|         return ResultPermissionDenied; | ||||
|     } | ||||
|  | ||||
|     got_vsync_event = true; | ||||
|  | ||||
|     return GetVSyncEventUnchecked(); | ||||
| } | ||||
|  | ||||
| Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { | ||||
|     return &vsync_event->GetReadableEvent(); | ||||
| } | ||||
|  | ||||
| void Display::SignalVSyncEvent() { | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
|  | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/result.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| class KEvent; | ||||
| @@ -73,8 +74,16 @@ public: | ||||
|         return layers.size(); | ||||
|     } | ||||
|  | ||||
|     /// Gets the readable vsync event. | ||||
|     Kernel::KReadableEvent& GetVSyncEvent(); | ||||
|     /** | ||||
|      * Gets the internal vsync event. | ||||
|      * | ||||
|      * @returns The internal Vsync event if it has not yet been retrieved, | ||||
|      *          VI::ResultPermissionDenied otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent(); | ||||
|  | ||||
|     /// Gets the internal vsync event. | ||||
|     Kernel::KReadableEvent* GetVSyncEventUnchecked(); | ||||
|  | ||||
|     /// Signals the internal vsync event. | ||||
|     void SignalVSyncEvent(); | ||||
| @@ -118,6 +127,7 @@ private: | ||||
|  | ||||
|     std::vector<std::unique_ptr<Layer>> layers; | ||||
|     Kernel::KEvent* vsync_event{}; | ||||
|     bool got_vsync_event{false}; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::VI | ||||
|   | ||||
| @@ -671,19 +671,23 @@ private: | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const u64 display_id = rp.Pop<u64>(); | ||||
|  | ||||
|         LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); | ||||
|         LOG_DEBUG(Service_VI, "called. display_id={}", display_id); | ||||
|  | ||||
|         const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); | ||||
|         if (!vsync_event) { | ||||
|         if (vsync_event.Failed()) { | ||||
|             const auto result = vsync_event.Code(); | ||||
|             if (result == ResultNotFound) { | ||||
|                 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); | ||||
|             } | ||||
|  | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ResultNotFound); | ||||
|             rb.Push(result); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(vsync_event); | ||||
|         rb.PushCopyObjects(*vsync_event); | ||||
|     } | ||||
|  | ||||
|     void ConvertScalingMode(Kernel::HLERequestContext& ctx) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user