AnyPS5/core/libs/tests/GuestVideoOutHighFrameRate.cpp

75 lines
3.9 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include "tests/VideoOutTestEnvironment.hpp"
#include <chrono>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <stdexcept>
extern "C" {
int APS5_VABI sceVideoOutOpen(int, int, int, const void*);
int APS5_VABI sceVideoOutClose(int);
int APS5_VABI sceVideoOutIsOutputSupported(int, std::uint64_t, const VideoOutOutputOptions*, void*, std::uint64_t);
int APS5_VABI sceVideoOutConfigureOutput(int, std::uint64_t, const VideoOutOutputOptions*, void*, std::uint64_t);
int APS5_VABI sceVideoOutWaitVblank(int);
int APS5_VABI sceVideoOutGetOutputStatus(int, VideoOutOutputStatus*);
int APS5_VABI sceVideoOutGetResolutionStatus(int, VideoOutResolutionStatus*);
}
static constexpr int Unsupported = static_cast<int>(0x80290016u);
static void Require(bool value, const char* message) {
if (!value) throw std::runtime_error(message);
}
static double Measure(int handle) {
Require(sceVideoOutWaitVblank(handle) == 0, "initial vblank failed");
const auto start = std::chrono::steady_clock::now();
for (int frame = 0; frame < 60; ++frame) Require(sceVideoOutWaitVblank(handle) == 0, "vblank failed");
return std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
}
int main(int argc, char** argv) {
const bool requireDisplay = argc > 1 && std::strcmp(argv[1], "--require-high-frame-rate") == 0;
VideoOutTestEnvironment environment;
{
std::ofstream param("app0/sce_sys/param.json", std::ios::binary);
param << R"({"titleId":"PPSA99007","localizedParameters":{"en-US":{"titleName":"High frame rate test"}},"attribute3":64})";
Require(static_cast<bool>(param), "cannot write high-frame-rate fixture");
}
int handle;
try {
handle = sceVideoOutOpen(255, 0, 0, nullptr);
} catch (const std::runtime_error& error) {
if (requireDisplay) throw;
std::printf("skipped, no display or Vulkan device: %s\n", error.what());
return 77;
}
const int supported = sceVideoOutIsOutputSupported(handle, 15, nullptr, nullptr, 0);
if (supported == Unsupported && !requireDisplay) {
Require(sceVideoOutConfigureOutput(handle, 15, nullptr, nullptr, 0) == Unsupported, "unsupported display accepted high-frame-rate output");
Require(sceVideoOutClose(handle) == 0, "close failed");
LibcRunShutdown_nid_postfix();
std::puts("skipped, current display does not provide 120 Hz");
return 77;
}
Require(supported == 1, "120 Hz output not reported as supported");
Require(sceVideoOutConfigureOutput(handle, 1, nullptr, nullptr, 0) == 0, "default mode failed");
const double normal = Measure(handle);
Require(sceVideoOutConfigureOutput(handle, 15, nullptr, nullptr, 0) == 0, "permitted high-frame-rate mode failed");
VideoOutOutputStatus output{};
VideoOutResolutionStatus resolution{};
Require(sceVideoOutGetOutputStatus(handle, &output) == 0 && output.refreshRate == 13, "wrong output refresh rate");
Require(sceVideoOutGetResolutionStatus(handle, &resolution) == 0 && resolution.refreshRate == 13, "wrong resolution refresh rate");
const double fast = Measure(handle);
Require(sceVideoOutConfigureOutput(handle, 1, nullptr, nullptr, 0) == 0, "default mode restore failed");
Require(sceVideoOutGetOutputStatus(handle, &output) == 0 && output.refreshRate == 3, "default output refresh rate not restored");
const double restored = Measure(handle);
std::printf("60 vblanks: default=%.3f s high=%.3f s restored=%.3f s\n", normal, fast, restored);
Require(normal > 0.8 && normal < 1.3 && fast > 0.4 && fast < 0.7 && restored > 0.8 && restored < 1.3, "incorrect vblank periods");
Require(normal / fast > 1.7 && restored / fast > 1.7, "high-frame-rate mode did not double vblank frequency");
Require(sceVideoOutClose(handle) == 0, "close failed");
LibcRunShutdown_nid_postfix();
}