mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
The guest video out had no way to tell a title that the display it presents is smaller than the one the title assumes: the buffer attribute the title writes was taken as it was, so a title that renders 3840x2160 and presents it in a 1920x1080 window pays for the 4K render and shows it letterboxed. ANYPS5_VIDEO_OUT_MAX_WIDTH and ANYPS5_VIDEO_OUT_MAX_HEIGHT clamp every buffer attribute the guest registers, through the set-attribute helpers or directly through sceVideoOutRegisterBuffers2 or sceVideoOutRegisterBuffers, and answer an output mode the host does not implement as unsupported when either limit is set, so the title registers a buffer the host can show and renders at that size. sceVideoOutConfigureOutput now treats that unsupported answer as VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE instead of proceeding to configure the mode. Each limit is a positive decimal integer; an unset, empty, malformed or zero value disables that axis. The two axes clamp independently, so a buffer whose aspect ratio is not 16:9 can change its presented aspect ratio.
167 lines
6.7 KiB
C++
167 lines
6.7 KiB
C++
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/Shutdown.hpp"
|
|
#include "prx/libSceVideoOut/include/Buffer.hpp"
|
|
#include "prx/libSceVideoOut/include/Output.hpp"
|
|
#include "prx/libSceVideoOut/include/VideoOutDriver.hpp"
|
|
#include "tests/VideoOutTestEnvironment.hpp"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#undef main
|
|
|
|
static constexpr int SYSTEM_USER = 255;
|
|
static constexpr int MAIN_BUS = 0;
|
|
static constexpr int UNSUPPORTED_OUTPUT_MODE = static_cast<int>(0x80290016);
|
|
static constexpr int UNKNOWN_OUTPUT_MODE = static_cast<int>(0x8029001E);
|
|
static constexpr int UNAVAILABLE_OUTPUT_MODE = static_cast<int>(0x80290019);
|
|
static constexpr std::uint64_t TILING_MODE_LINEAR = 0;
|
|
static constexpr std::uint64_t PIXEL_FORMAT = 0x8000000022000000ull;
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
static void SetEnvironment(const char* name, const char* value) {
|
|
#ifdef _WIN32
|
|
Require(_putenv_s(name, value) == 0);
|
|
#else
|
|
Require(setenv(name, value, 1) == 0);
|
|
#endif
|
|
}
|
|
|
|
static void UnsetEnvironment(const char* name) {
|
|
#ifdef _WIN32
|
|
Require(_putenv_s(name, "") == 0);
|
|
#else
|
|
Require(unsetenv(name) == 0);
|
|
#endif
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
const std::string_view mode = argc > 1 ? std::string_view(argv[1]) : std::string_view{};
|
|
const bool heightOnly = mode == "height-only";
|
|
const bool widthOnly = mode == "width-only";
|
|
const bool fourK = mode == "four-k";
|
|
const bool malformed = mode == "malformed";
|
|
const bool disabled = mode == "disabled";
|
|
if (disabled || heightOnly) {
|
|
UnsetEnvironment("ANYPS5_VIDEO_OUT_MAX_WIDTH");
|
|
} else if (fourK) {
|
|
SetEnvironment("ANYPS5_VIDEO_OUT_MAX_WIDTH", "3840");
|
|
} else if (malformed) {
|
|
SetEnvironment("ANYPS5_VIDEO_OUT_MAX_WIDTH", "1920px");
|
|
} else {
|
|
SetEnvironment("ANYPS5_VIDEO_OUT_MAX_WIDTH", "1920");
|
|
}
|
|
if (disabled || widthOnly) {
|
|
UnsetEnvironment("ANYPS5_VIDEO_OUT_MAX_HEIGHT");
|
|
} else if (fourK) {
|
|
SetEnvironment("ANYPS5_VIDEO_OUT_MAX_HEIGHT", "2160");
|
|
} else {
|
|
SetEnvironment("ANYPS5_VIDEO_OUT_MAX_HEIGHT", "1080");
|
|
}
|
|
|
|
VideoOutTestEnvironment environment;
|
|
VideoOutOutputOptions options{};
|
|
Require(sceVideoOutInitializeOutputOptions(&options) == 0);
|
|
int handle = 0;
|
|
try {
|
|
handle = sceVideoOutOpen(SYSTEM_USER, MAIN_BUS, 0, nullptr);
|
|
} catch (const std::runtime_error& error) {
|
|
if (std::getenv("ANYPS5_REQUIRE_DISPLAY") != nullptr) throw;
|
|
std::printf("skipped, no display or Vulkan device: %s\n", error.what());
|
|
return 77;
|
|
}
|
|
Require(handle > 0);
|
|
|
|
Require(VideoOutOutputSupport_nid_no_patch(1, &options, 60) == 1);
|
|
Require(VideoOutOutputSupport_nid_no_patch(15, &options, 59) == 0);
|
|
Require(VideoOutOutputSupport_nid_no_patch(15, &options, 120) == 1);
|
|
Require(VideoOutOutputSupport_nid_no_patch(0x4ull, &options, 120) == UNSUPPORTED_OUTPUT_MODE);
|
|
Require(VideoOutOutputSupport_nid_no_patch(0xD000000Aull, &options, 120) == UNKNOWN_OUTPUT_MODE);
|
|
Require(sceVideoOutIsOutputSupported(handle, 1, &options, nullptr, 0) == 1);
|
|
Require(sceVideoOutConfigureOutput(handle, 1, &options, nullptr, 0) == 0);
|
|
for (std::uint64_t unknownMode : {0xD000000Aull, 0x8000000000000001ull, ~0ull}) {
|
|
if (malformed) continue;
|
|
if (disabled || fourK) {
|
|
Require(sceVideoOutIsOutputSupported(handle, unknownMode, &options, nullptr, 0) == UNKNOWN_OUTPUT_MODE);
|
|
Require(sceVideoOutConfigureOutput(handle, unknownMode, &options, nullptr, 0) == UNKNOWN_OUTPUT_MODE);
|
|
} else {
|
|
Require(sceVideoOutIsOutputSupported(handle, unknownMode, &options, nullptr, 0) == 0);
|
|
Require(sceVideoOutConfigureOutput(handle, unknownMode, &options, nullptr, 0) == UNAVAILABLE_OUTPUT_MODE);
|
|
}
|
|
}
|
|
for (std::uint64_t mode : {0x4ull, 0x7ull, 0x8ull, 0xCull, 0xDull, 0xEull, 0x10ull, 0x11ull, 0x13ull}) {
|
|
Require(sceVideoOutIsOutputSupported(handle, mode, &options, nullptr, 0) == UNSUPPORTED_OUTPUT_MODE);
|
|
}
|
|
|
|
VideoOutBufferAttribute2 attribute{};
|
|
sceVideoOutSetBufferAttribute2(&attribute, PIXEL_FORMAT, TILING_MODE_LINEAR, 3840, 2160, 0, 0, 0);
|
|
Require(attribute.width == 3840);
|
|
Require(attribute.height == 2160);
|
|
sceVideoOutSetBufferAttribute2(&attribute, PIXEL_FORMAT, TILING_MODE_LINEAR, 1280, 720, 0, 0, 0);
|
|
Require(attribute.width == 1280);
|
|
Require(attribute.height == 720);
|
|
|
|
VideoOutBufferAttribute ps4{};
|
|
sceVideoOutSetBufferAttribute(&ps4, 0x80002200u, 1, 0, 3840, 2160, 0);
|
|
Require(ps4.width == 3840);
|
|
Require(ps4.height == 2160);
|
|
|
|
std::vector<std::byte> allocation(64u * 1024u * 1024u);
|
|
const auto address = reinterpret_cast<std::uintptr_t>(allocation.data());
|
|
const auto aligned = (address + 65535u) & ~static_cast<std::uintptr_t>(65535u);
|
|
VideoOutBuffers buffer{reinterpret_cast<void*>(aligned), nullptr, {nullptr, nullptr}};
|
|
VideoOutBufferAttribute2 direct{};
|
|
direct.width = 3840;
|
|
direct.height = 2160;
|
|
direct.pixel_format = PIXEL_FORMAT;
|
|
direct.tiling_mode = TILING_MODE_LINEAR;
|
|
Require(sceVideoOutRegisterBuffers2(handle, 0, 0, &buffer, 1, &direct, 0, nullptr) == 0);
|
|
auto cfg = VideoOutDriver::Get().GetConfig(handle);
|
|
Require(cfg != nullptr);
|
|
{
|
|
std::lock_guard lock(cfg->mutex);
|
|
Require(cfg->groups[0].attribute.width == 3840);
|
|
Require(cfg->groups[0].attribute.height == 2160);
|
|
Require(cfg->width == 3840);
|
|
Require(cfg->height == 2160);
|
|
}
|
|
|
|
VideoOutBufferAttribute2 changed{};
|
|
changed.width = 3840;
|
|
changed.height = 2160;
|
|
changed.pixel_format = PIXEL_FORMAT;
|
|
changed.tiling_mode = TILING_MODE_LINEAR;
|
|
Require(sceVideoOutSubmitChangeBufferAttribute2(handle, 0, &changed, nullptr) == 0);
|
|
{
|
|
std::lock_guard lock(cfg->mutex);
|
|
Require(cfg->groups[0].attribute.width == 3840);
|
|
Require(cfg->groups[0].attribute.height == 2160);
|
|
}
|
|
|
|
if (malformed) {
|
|
bool supportedThrew = false;
|
|
try {
|
|
(void)sceVideoOutIsOutputSupported(handle, 0xD000000Aull, &options, nullptr, 0);
|
|
} catch (const std::runtime_error& error) {
|
|
supportedThrew = std::string_view(error.what()).find("ANYPS5_VIDEO_OUT_MAX_WIDTH") != std::string_view::npos;
|
|
}
|
|
Require(supportedThrew);
|
|
bool configureThrew = false;
|
|
try {
|
|
(void)sceVideoOutConfigureOutput(handle, 0xD000000Aull, &options, nullptr, 0);
|
|
} catch (const std::runtime_error& error) {
|
|
configureThrew = std::string_view(error.what()).find("ANYPS5_VIDEO_OUT_MAX_WIDTH") != std::string_view::npos;
|
|
}
|
|
Require(configureThrew);
|
|
}
|
|
|
|
Require(sceVideoOutClose(handle) == 0);
|
|
LibcRunShutdown_nid_postfix();
|
|
}
|