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:
Morph 2022-09-25 21:20:36 -04:00
parent acc887cc34
commit 41e855bd42
5 changed files with 42 additions and 14 deletions

View File

@ -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) {

View File

@ -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.

View File

@ -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() {

View File

@ -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

View File

@ -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) {
LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
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) {