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/nvflinger/ui/graphic_buffer.h" | ||||||
| #include "core/hle/service/vi/display/vi_display.h" | #include "core/hle/service/vi/display/vi_display.h" | ||||||
| #include "core/hle/service/vi/layer/vi_layer.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/gpu.h" | ||||||
|  |  | ||||||
| namespace Service::NVFlinger { | namespace Service::NVFlinger { | ||||||
| @@ -163,15 +164,15 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { | |||||||
|     return layer->GetBinderId(); |     return layer->GetBinderId(); | ||||||
| } | } | ||||||
|  |  | ||||||
| Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { | ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) { | ||||||
|     const auto lock_guard = Lock(); |     const auto lock_guard = Lock(); | ||||||
|     auto* const display = FindDisplay(display_id); |     auto* const display = FindDisplay(display_id); | ||||||
|  |  | ||||||
|     if (display == nullptr) { |     if (display == nullptr) { | ||||||
|         return nullptr; |         return VI::ResultNotFound; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return &display->GetVSyncEvent(); |     return display->GetVSyncEvent(); | ||||||
| } | } | ||||||
|  |  | ||||||
| VI::Display* NVFlinger::FindDisplay(u64 display_id) { | VI::Display* NVFlinger::FindDisplay(u64 display_id) { | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
| #include "core/hle/service/kernel_helpers.h" | #include "core/hle/service/kernel_helpers.h" | ||||||
|  |  | ||||||
| namespace Common { | namespace Common { | ||||||
| @@ -71,8 +72,9 @@ public: | |||||||
|  |  | ||||||
|     /// Gets the vsync event for the specified display. |     /// Gets the vsync event for the specified display. | ||||||
|     /// |     /// | ||||||
|     /// If an invalid display ID is provided, then nullptr is returned. |     /// If an invalid display ID is provided, then VI::ResultNotFound is returned. | ||||||
|     [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); |     /// 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 |     /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | ||||||
|     /// finished. |     /// finished. | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ | |||||||
| #include "core/hle/service/nvflinger/hos_binder_driver_server.h" | #include "core/hle/service/nvflinger/hos_binder_driver_server.h" | ||||||
| #include "core/hle/service/vi/display/vi_display.h" | #include "core/hle/service/vi/display/vi_display.h" | ||||||
| #include "core/hle/service/vi/layer/vi_layer.h" | #include "core/hle/service/vi/layer/vi_layer.h" | ||||||
|  | #include "core/hle/service/vi/vi_results.h" | ||||||
|  |  | ||||||
| namespace Service::VI { | namespace Service::VI { | ||||||
|  |  | ||||||
| @@ -55,8 +56,18 @@ const Layer& Display::GetLayer(std::size_t index) const { | |||||||
|     return *layers.at(index); |     return *layers.at(index); | ||||||
| } | } | ||||||
|  |  | ||||||
| Kernel::KReadableEvent& Display::GetVSyncEvent() { | ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { | ||||||
|     return vsync_event->GetReadableEvent(); |     if (got_vsync_event) { | ||||||
|  |         return ResultPermissionDenied; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     got_vsync_event = true; | ||||||
|  |  | ||||||
|  |     return GetVSyncEventUnchecked(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { | ||||||
|  |     return &vsync_event->GetReadableEvent(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Display::SignalVSyncEvent() { | void Display::SignalVSyncEvent() { | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
|  |  | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
|  |  | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| class KEvent; | class KEvent; | ||||||
| @@ -73,8 +74,16 @@ public: | |||||||
|         return layers.size(); |         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. |     /// Signals the internal vsync event. | ||||||
|     void SignalVSyncEvent(); |     void SignalVSyncEvent(); | ||||||
| @@ -118,6 +127,7 @@ private: | |||||||
|  |  | ||||||
|     std::vector<std::unique_ptr<Layer>> layers; |     std::vector<std::unique_ptr<Layer>> layers; | ||||||
|     Kernel::KEvent* vsync_event{}; |     Kernel::KEvent* vsync_event{}; | ||||||
|  |     bool got_vsync_event{false}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace Service::VI | } // namespace Service::VI | ||||||
|   | |||||||
| @@ -671,19 +671,23 @@ private: | |||||||
|         IPC::RequestParser rp{ctx}; |         IPC::RequestParser rp{ctx}; | ||||||
|         const u64 display_id = rp.Pop<u64>(); |         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); |         const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); | ||||||
|         if (!vsync_event) { |         if (vsync_event.Failed()) { | ||||||
|             LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); |             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}; |             IPC::ResponseBuilder rb{ctx, 2}; | ||||||
|             rb.Push(ResultNotFound); |             rb.Push(result); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; |         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||||
|         rb.Push(ResultSuccess); |         rb.Push(ResultSuccess); | ||||||
|         rb.PushCopyObjects(vsync_event); |         rb.PushCopyObjects(*vsync_event); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void ConvertScalingMode(Kernel::HLERequestContext& ctx) { |     void ConvertScalingMode(Kernel::HLERequestContext& ctx) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user