mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-21 16:12:47 -05:00
sceVideoOutOpen with bus type 0x20, the PS VR2 output PPSA05666 opens as SCE_VIDEO_OUT_BUS_TYPE_HMD2_MAIN after sceHmd2ReprojectionEnableVrMode, threw VIDEO_OUT_ERROR_INVALID_VALUE and aborted the title. The console returns that code to a process that is not a game, with the headset connected and worn or not, for users 255 and 0 and for a null or zeroed open parameter, so the bus is answered that way before the other argument checks and the title carries on with the error as its handle, as it does on the console without a headset. The debt entry records the sequence the title follows on that handle (RegisterBuffers2 with category 16 and 4000x2040 descriptors that point at Hmd2 per-eye textures, ConfigureOutput and IsOutputSupported with 0x2000F, a GPU wait on buffer 0), which a VR backend will have to model, and the test covers the bus and the HMD output modes 0x20001 and 0x2000C to 0x20010, which the console and this implementation answer with VIDEO_OUT_ERROR_UNKNOWN_OUTPUT_MODE on the TV bus. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
261 lines
9.3 KiB
C++
261 lines
9.3 KiB
C++
#ifndef CORE_LIBS_PRX_LIBSCEVIDEOOUT_INCLUDE_VIDEOOUTDRIVER_HPP
|
|
#define CORE_LIBS_PRX_LIBSCEVIDEOOUT_INCLUDE_VIDEOOUTDRIVER_HPP
|
|
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <list>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include <atomic>
|
|
#include <exception>
|
|
#include <future>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp"
|
|
#include "prx/libSceAgcDriver/Execution/include/DisplayBuffer.hpp"
|
|
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
|
|
#include "prx/libSceVideoOut/include/BufferReuseTracker.hpp"
|
|
#include "prx/libSceVideoOut/include/HostHdrProbe.hpp"
|
|
#include "prx/libc/include/Shutdown.hpp"
|
|
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
|
|
|
#include "SDL.h"
|
|
#include "SceTypes.hpp"
|
|
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_VALUE = -2144796671;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_ADDRESS = -2144796670;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_HANDLE = -2144796661;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = -2144796660;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_INDEX = -2144796662;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_OPTION = -2144796646;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_CATEGORY = -2144796643;
|
|
static constexpr int VIDEO_OUT_ERROR_SLOT_OCCUPIED = -2144796656;
|
|
static constexpr int VIDEO_OUT_ERROR_RESOURCE_BUSY = -2144796663;
|
|
static constexpr int VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = -2144796654;
|
|
static constexpr int VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = -2144796650;
|
|
static constexpr int VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = -2144796647;
|
|
static constexpr int VIDEO_OUT_ERROR_INVALID_EVENT = -2144796659;
|
|
static constexpr int VIDEO_OUT_ERROR_UNKNOWN_OUTPUT_MODE = -2144796642;
|
|
|
|
static constexpr int VIDEO_OUT_BUS_TYPE_MAIN = 0;
|
|
static constexpr int VIDEO_OUT_BUS_TYPE_OVERLAY = 1;
|
|
static constexpr int VIDEO_OUT_BUS_TYPE_SUB = 2;
|
|
static constexpr int VIDEO_OUT_BUS_TYPE_HMD2_MAIN = 0x20;
|
|
|
|
static constexpr uint32_t VIDEO_OUT_DEFAULT_WIDTH = 1920;
|
|
static constexpr uint32_t VIDEO_OUT_DEFAULT_HEIGHT = 1080;
|
|
|
|
static constexpr std::uint32_t VIDEO_OUT_OPEN_PARAM_FIRST_WORD = 16;
|
|
static constexpr std::int32_t VIDEO_OUT_SERVICE_THREAD_PRIORITY_HIGHEST = 256;
|
|
static constexpr std::int32_t VIDEO_OUT_SERVICE_THREAD_PRIORITY_LOWEST = 767;
|
|
static constexpr std::uint64_t VIDEO_OUT_SERVICE_THREAD_AFFINITY_ALL = 0x1FFF;
|
|
|
|
struct VideoOutOpenParam {
|
|
std::uint32_t firstWord;
|
|
std::uint32_t setPriority;
|
|
std::int32_t priority;
|
|
std::uint32_t setAffinity;
|
|
std::uint64_t affinity;
|
|
};
|
|
static_assert(offsetof(VideoOutOpenParam, affinity) == 16);
|
|
|
|
static constexpr int VIDEO_OUT_BUFFER_NUM_MAX = 16;
|
|
static constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX = 4;
|
|
static constexpr int VIDEO_OUT_NUM_MAX = 4;
|
|
static constexpr std::size_t VIDEO_OUT_FLIP_QUEUE_CAPACITY = 16;
|
|
|
|
static constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_UNCOMPRESSED = 0;
|
|
static constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_COMPRESSED = 1;
|
|
static constexpr std::uint64_t VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE = 0;
|
|
static constexpr std::uint64_t VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8;
|
|
static constexpr std::uint32_t VIDEO_OUT_DCC_CONTROL_BLOCK_LAYOUT = 0x10026c;
|
|
|
|
static constexpr int VIDEO_OUT_EVENT_FLIP = 0;
|
|
static constexpr int VIDEO_OUT_EVENT_VBLANK = 1;
|
|
static constexpr int VIDEO_OUT_EVENT_PRE_VBLANK_START = 2;
|
|
static constexpr int VIDEO_OUT_EVENT_SET_MODE = 8;
|
|
static constexpr int VIDEO_OUT_EVENT_VRR_STATUS = 16;
|
|
|
|
static constexpr int VIDEO_OUT_FLIP_MODE_VSYNC = 1;
|
|
static constexpr int VIDEO_OUT_FLIP_MODE_VSYNC_MULTI = 4;
|
|
|
|
static constexpr int VIDEO_OUT_BUFFER_INDEX_BLACK = -2;
|
|
static constexpr int VIDEO_OUT_BUFFER_INDEX_BLANK = -1;
|
|
|
|
static constexpr uint64_t VIDEO_OUT_OUTPUT_MODE_DEFAULT = 0x0000000000000001ULL;
|
|
static constexpr uint64_t VIDEO_OUT_OUTPUT_MODE_119_88HZ = 0x000000000000000FULL;
|
|
|
|
static constexpr std::uint32_t VIDEO_OUT_DYNAMIC_RANGE_SDR = 1;
|
|
static constexpr std::uint32_t VIDEO_OUT_DYNAMIC_RANGE_HDR = 2;
|
|
static constexpr std::uint64_t VIDEO_OUT_OUTPUT_STATUS_FLAG_HDR = 1;
|
|
|
|
static constexpr uint64_t VIDEO_OUT_REFRESH_RATE_59_94HZ = 3;
|
|
static constexpr uint64_t VIDEO_OUT_REFRESH_RATE_119_88HZ = 13;
|
|
|
|
struct VideoOutBuffer {
|
|
int groupIndex = -1;
|
|
uint64_t dataAddress = 0;
|
|
uint64_t metadataAddress = 0;
|
|
|
|
bool Occupied() const { return groupIndex >= 0; }
|
|
};
|
|
|
|
struct BufferAttributeGroup {
|
|
VideoOutBufferAttribute2 attribute{};
|
|
int category = VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_UNCOMPRESSED;
|
|
bool occupied = false;
|
|
bool ps4 = false;
|
|
};
|
|
|
|
AgcDriver::DisplayBuffer DescribeVideoOutBuffer(const VideoOutBuffer& buffer, const BufferAttributeGroup& group);
|
|
|
|
struct EventRegistration {
|
|
KernelEqueue eq = 0;
|
|
uint64_t generation = 0;
|
|
};
|
|
|
|
struct VideoOutConfig {
|
|
std::mutex mutex;
|
|
std::condition_variable_any vblankCond;
|
|
|
|
std::vector<EventRegistration> flipEvents;
|
|
std::vector<EventRegistration> vblankEvents;
|
|
std::vector<EventRegistration> preVblankEvents;
|
|
std::vector<EventRegistration> outputModeEvents;
|
|
std::vector<EventRegistration> vrrStatusEvents;
|
|
|
|
uint32_t width = VIDEO_OUT_DEFAULT_WIDTH;
|
|
uint32_t height = VIDEO_OUT_DEFAULT_HEIGHT;
|
|
uint64_t generation = 0;
|
|
int busType = VIDEO_OUT_BUS_TYPE_MAIN;
|
|
bool opened = false;
|
|
bool closing = false;
|
|
bool ps4 = false;
|
|
std::exception_ptr failure;
|
|
std::stop_token shutdownToken = LibcShutdownToken_nid_postfix();
|
|
int flipRate = 0;
|
|
uint64_t lastFlipVblank = 0;
|
|
std::chrono::steady_clock::time_point lastTimingFlip{};
|
|
uint64_t outputMode = VIDEO_OUT_OUTPUT_MODE_DEFAULT;
|
|
float gamma = 1.0f;
|
|
|
|
VideoOutFlipStatus flipStatus{};
|
|
VideoOutVblankStatus vblankStatus{};
|
|
VideoOutVblankStatus preVblankStatus{};
|
|
|
|
std::array<VideoOutBuffer, VIDEO_OUT_BUFFER_NUM_MAX> buffers{};
|
|
alignas(std::atomic_ref<std::uint64_t>::required_alignment) std::array<std::uint64_t, VIDEO_OUT_BUFFER_NUM_MAX> bufferLabels{};
|
|
std::array<uint32_t, VIDEO_OUT_BUFFER_NUM_MAX> bufferPending{};
|
|
std::array<BufferReuseTracker, VIDEO_OUT_BUFFER_NUM_MAX> bufferReuse;
|
|
std::array<BufferAttributeGroup, VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX> groups{};
|
|
|
|
bool Closed() const { return !opened || closing; }
|
|
|
|
void CheckAlive() const {
|
|
if (failure) std::rethrow_exception(failure);
|
|
if (shutdownToken.stop_requested()) throw ProcessShutdown{};
|
|
}
|
|
|
|
void Check() const {
|
|
CheckAlive();
|
|
if (Closed()) throw std::runtime_error("VideoOut: port is closed");
|
|
}
|
|
};
|
|
|
|
struct FlipQueue;
|
|
|
|
struct FlipRequest final : AgcDriver::IFlipRequest, std::enable_shared_from_this<FlipRequest> {
|
|
std::uint64_t reuseTicket = 0;
|
|
std::shared_ptr<VideoOutConfig> cfg;
|
|
std::shared_ptr<FlipQueue> queue;
|
|
uint64_t generation = 0;
|
|
std::uint32_t outputHandle = 0;
|
|
int index = 0;
|
|
int flipMode = 0;
|
|
int flipRate = 0;
|
|
int64_t flipArg = 0;
|
|
uint32_t width = 0;
|
|
uint32_t height = 0;
|
|
VideoOutBuffer buffer;
|
|
BufferAttributeGroup group;
|
|
bool groupCaptured = false;
|
|
bool reserved = false;
|
|
bool unregistered = false;
|
|
bool ready = false;
|
|
bool gpuComplete = false;
|
|
bool terminal = false;
|
|
|
|
std::shared_ptr<AgcDriver::FrameTiming> timing;
|
|
std::chrono::steady_clock::time_point queuedAt;
|
|
|
|
~FlipRequest() override;
|
|
void GpuReady(const std::shared_ptr<AgcDriver::FrameTiming>& frameTiming) override;
|
|
void Fail(std::exception_ptr error) noexcept override;
|
|
void Cancel() noexcept;
|
|
void ReleaseLocked() noexcept;
|
|
};
|
|
|
|
struct FlipQueue {
|
|
std::mutex mutex;
|
|
TimedWait::Condition changed;
|
|
std::list<std::shared_ptr<FlipRequest>> requests;
|
|
std::atomic<std::size_t> reservations = 0;
|
|
std::exception_ptr failure;
|
|
bool stopping = false;
|
|
};
|
|
|
|
class VideoOutDriver {
|
|
public:
|
|
static VideoOutDriver& Get();
|
|
|
|
VideoOutDriver();
|
|
~VideoOutDriver();
|
|
void Shutdown();
|
|
|
|
VideoOutDriver(const VideoOutDriver&) = delete;
|
|
VideoOutDriver& operator=(const VideoOutDriver&) = delete;
|
|
|
|
int Open(int busType);
|
|
bool Close(int handle);
|
|
std::shared_ptr<VideoOutConfig> GetConfig(int handle);
|
|
bool IsOpen(int handle);
|
|
bool HasConfig(int handle);
|
|
bool SupportsHighFrameRate();
|
|
int GetDisplayRefreshRate();
|
|
|
|
// 0, or VIDEO_OUT_ERROR_FLIP_QUEUE_FULL when the title has VIDEO_OUT_FLIP_QUEUE_CAPACITY flips pending.
|
|
int SubmitFlip(int handle, int index, int flipMode, int64_t flipArg);
|
|
int SubmitEopFlip(int handle, int index, int flipMode, int64_t flipArg, std::uint32_t* token);
|
|
|
|
private:
|
|
bool close(int handle);
|
|
void presentLoop(std::stop_token token, std::promise<void>& started);
|
|
void vblankLoop(std::stop_token token);
|
|
void vblankEnd(std::uint64_t tick);
|
|
void updateDisplayRefreshRate();
|
|
void processFlip(FlipRequest& req);
|
|
void triggerEvents(VideoOutConfig& cfg, int eventKind, void* triggerData);
|
|
void pollHostHdr();
|
|
|
|
std::mutex mutex;
|
|
std::mutex shutdownMutex;
|
|
bool stopped = false;
|
|
std::array<std::shared_ptr<VideoOutConfig>, VIDEO_OUT_NUM_MAX> contexts;
|
|
std::array<std::shared_ptr<AgcDriver::IVideoOutput>, VIDEO_OUT_NUM_MAX> outputs;
|
|
std::shared_ptr<FlipQueue> flipQueue = std::make_shared<FlipQueue>();
|
|
|
|
DisplayWindow window;
|
|
HostHdrProbe hdrProbe;
|
|
std::uint64_t hdrGeneration = 0;
|
|
std::atomic<int> displayRefreshRate{0};
|
|
|
|
std::jthread presentThread;
|
|
std::jthread vblankThread;
|
|
};
|
|
|
|
#endif
|