feat(sceVideoOut): video out functions impl (wip)

This commit is contained in:
boykopovar 2026-09-12 11:02:17 +03:00
parent 69b8080ea4
commit 497821e94f
10 changed files with 632 additions and 167 deletions

View File

@ -1942,6 +1942,11 @@ struct VideoOutOutputStatus {
struct VideoOutOutputOptions { std::uint32_t internalData[16] = {}; };
struct VideoOutColorSettings {
float gamma = 1.0f;
std::uint32_t reserved[3] = {};
};
using atexit_func_t = void (*)();

View File

@ -2,6 +2,7 @@ add_library(libSceVideoOut SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/src/Event.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Output.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/VideoOutDriver.cpp
)
target_include_directories(libSceVideoOut PRIVATE ${LIBS_INCLUDE_DIR})

View File

@ -1,5 +1,5 @@
#ifndef CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_OUTPUT_HPP
#define CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_OUTPUT_HPP
#ifndef CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_BUFFER_HPP
#define CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_BUFFER_HPP
#include <cstdint>

View File

@ -19,7 +19,11 @@ int APS5_VABI sceVideoOutGetOutputStatus(int handle, VideoOutOutputStatus* statu
int APS5_VABI sceVideoOutGetVblankStatus(int handle, VideoOutVblankStatus* status);
int APS5_VABI sceVideoOutIsFlipPending(int handle);
int APS5_VABI sceVideoOutWaitVblank(int handle);
#endif
int APS5_VABI sceVideoOutLatencyControlWaitBeforeInput(int handle);
int APS5_VABI sceVideoOutLatencyMeasureSetStartPoint(int handle, uint32_t point);
int APS5_VABI sceVideoOutColorSettingsSetGamma(VideoOutColorSettings* settings, float gamma);
int APS5_VABI sceVideoOutAdjustColor(int handle, const VideoOutColorSettings* settings);
}
#endif

View File

@ -0,0 +1,100 @@
#ifndef CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_VIDEOOUTDRIVER_HPP
#define CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_VIDEOOUTDRIVER_HPP
#include <array>
#include <condition_variable>
#include <cstdint>
#include <mutex>
#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 = -2144796669;
static constexpr int VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = -2144796668;
static constexpr int VIDEO_OUT_ERROR_INVALID_INDEX = -2144796667;
static constexpr int VIDEO_OUT_ERROR_INVALID_OPTION = -2144796666;
static constexpr int VIDEO_OUT_ERROR_INVALID_CATEGORY = -2144796664;
static constexpr int VIDEO_OUT_ERROR_SLOT_OCCUPIED = -2144796663;
static constexpr int VIDEO_OUT_ERROR_RESOURCE_BUSY = -2144796656;
static constexpr int VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = -2144796654;
static constexpr int VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = -2144796634;
static constexpr int VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = -2144796633;
static constexpr int VIDEO_OUT_ERROR_INVALID_EVENT = -2144796624;
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_BUFFER_NUM_MAX = 16;
static constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX = 4;
static constexpr int VIDEO_OUT_NUM_MAX = 4;
static constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_UNCOMPRESSED = 0;
static constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_COMPRESSED = 1;
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_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 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;
};
struct VideoOutConfig {
std::mutex mutex;
std::condition_variable_any vblankCond;
uint32_t width = 1920;
uint32_t height = 1080;
uint64_t generation = 0;
bool opened = false;
bool closing = false;
int flipRate = 0;
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{};
std::array<BufferAttributeGroup, VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX> groups{};
};
struct VideoOutDriver {
std::mutex mutex;
VideoOutConfig contexts[VIDEO_OUT_NUM_MAX];
static VideoOutDriver& Get();
int Open(int busType);
bool Close(int handle);
VideoOutConfig* GetConfig(int handle);
bool IsOpen(int handle);
};
#endif

View File

@ -1,57 +1,145 @@
#include <cstdint>
#include "SceTypes.hpp"
#include <cstring>
#include <mutex>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libSceVideoOut/include/Buffer.hpp"
#include "prx/libSceVideoOut/include/VideoOutDriver.hpp"
extern "C" {
int APS5_VABI sceVideoOutRegisterBuffers2(int handle, int set_index, int buffer_index_start, const VideoOutBuffers* buffers, int buffer_num, const VideoOutBufferAttribute2* attribute, int category, void* option) {
(void)handle;
(void)set_index;
(void)buffer_index_start;
(void)buffers;
(void)buffer_num;
(void)attribute;
(void)category;
(void)option;
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI sceVideoOutSetBufferAttribute2(VideoOutBufferAttribute2* attribute, uint64_t pixel_format, uint32_t tiling_mode, uint32_t width, uint32_t height, uint64_t option, uint32_t dcc_control, uint64_t dcc_cb_register_clear_color) {
if (attribute == nullptr) {
return;
}
std::memset(attribute, 0, sizeof(VideoOutBufferAttribute2));
attribute->tiling_mode = tiling_mode;
attribute->aspect_ratio = 0;
attribute->width = width;
attribute->height = height;
attribute->pitch_in_pixel = 0;
attribute->option = option;
attribute->pixel_format = pixel_format;
attribute->dcc_cb_register_clear_color = dcc_cb_register_clear_color;
attribute->dcc_control = dcc_control;
}
void APS5_VABI sceVideoOutSetBufferAttribute2(VideoOutBufferAttribute2* attribute, uint64_t pixel_format, uint32_t tiling_mode, uint32_t width, uint32_t height, uint64_t option, uint32_t dcc_control, uint64_t dcc_cb_register_clear_color) {
(void)attribute;
(void)pixel_format;
(void)tiling_mode;
(void)width;
(void)height;
(void)option;
(void)dcc_control;
(void)dcc_cb_register_clear_color;
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceVideoOutRegisterBuffers2(int handle, int set_index, int buffer_index_start, const VideoOutBuffers* buffers, int buffer_num, const VideoOutBufferAttribute2* attribute, int category, void* option) {
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (buffers == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
if (attribute == nullptr) {
return VIDEO_OUT_ERROR_INVALID_OPTION;
}
if (option != nullptr) {
return VIDEO_OUT_ERROR_INVALID_OPTION;
}
if (set_index < 0 || set_index >= VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX || buffer_index_start < 0 || buffer_index_start >= VIDEO_OUT_BUFFER_NUM_MAX || buffer_num < 1 || buffer_num > VIDEO_OUT_BUFFER_NUM_MAX || buffer_index_start + buffer_num > VIDEO_OUT_BUFFER_NUM_MAX) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (category != VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_UNCOMPRESSED && category != VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_COMPRESSED) {
return VIDEO_OUT_ERROR_INVALID_CATEGORY;
}
std::unique_lock lock(cfg->mutex);
if (cfg->closing) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (cfg->groups[set_index].occupied) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
for (int i = 0; i < buffer_num; i++) {
if (cfg->buffers[buffer_index_start + i].Occupied()) {
return VIDEO_OUT_ERROR_SLOT_OCCUPIED;
}
}
BufferAttributeGroup group{};
group.attribute = *attribute;
group.category = category;
group.occupied = true;
cfg->groups[set_index] = group;
for (int i = 0; i < buffer_num; i++) {
VideoOutBuffer buf{};
buf.groupIndex = set_index;
buf.dataAddress = reinterpret_cast<uint64_t>(buffers[i].data);
buf.metadataAddress = reinterpret_cast<uint64_t>(buffers[i].metadata);
cfg->buffers[buffer_index_start + i] = buf;
}
return 0;
}
int APS5_VABI sceVideoOutSubmitChangeBufferAttribute2(int handle, int set_index, const VideoOutBufferAttribute2* attribute, void* option) {
(void)handle;
(void)set_index;
(void)attribute;
(void)option;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg) {
(void)handle;
(void)index;
(void)flip_mode;
(void)flip_arg;
NotImplemented_nid_no_patch(__func__);
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (attribute == nullptr) {
return VIDEO_OUT_ERROR_INVALID_OPTION;
}
if (set_index < 0 || set_index >= VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
if (option != nullptr) {
return VIDEO_OUT_ERROR_INVALID_OPTION;
}
std::unique_lock lock(cfg->mutex);
if (cfg->closing || !cfg->groups[set_index].occupied) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
cfg->groups[set_index].attribute = *attribute;
return 0;
}
int APS5_VABI sceVideoOutUnregisterBuffers(int handle, int set_index) {
(void)handle;
(void)set_index;
NotImplemented_nid_no_patch(__func__);
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (set_index < 0 || set_index >= VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
std::unique_lock lock(cfg->mutex);
if (cfg->closing) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (!cfg->groups[set_index].occupied) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
cfg->groups[set_index] = BufferAttributeGroup{};
for (auto& buf : cfg->buffers) {
if (buf.groupIndex == set_index) {
buf = VideoOutBuffer{};
}
}
return 0;
}
int APS5_VABI sceVideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg) {
(void)flip_arg;
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (flip_mode < VIDEO_OUT_FLIP_MODE_VSYNC || flip_mode > VIDEO_OUT_FLIP_MODE_VSYNC_MULTI) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (index < VIDEO_OUT_BUFFER_INDEX_BLACK || index >= VIDEO_OUT_BUFFER_NUM_MAX) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
std::unique_lock lock(cfg->mutex);
if (cfg->closing) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
const bool special = (index == VIDEO_OUT_BUFFER_INDEX_BLANK || index == VIDEO_OUT_BUFFER_INDEX_BLACK);
if (!special && !cfg->buffers[index].Occupied()) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
cfg->flipStatus.currentBuffer = index;
cfg->flipStatus.flipArg = flip_arg;
cfg->flipStatus.count++;
return 0;
}

View File

@ -1,79 +1,146 @@
#include <mutex>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "../include/Event.hpp"
#include "prx/libkernel/Equeue/Equeue.hpp"
#include "prx/libSceVideoOut/include/Event.hpp"
#include "prx/libSceVideoOut/include/VideoOutDriver.hpp"
static int RegisterVideoOutEvent(int handle, KernelEqueue eq, int16_t eventKind, void* udata) {
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (eq == 0) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
if (!EqueuePin(eq)) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
KernelEqueueEvent event{};
event.event.ident = static_cast<uintptr_t>(eventKind);
event.event.filter = EVFILT_VIDEO_OUT;
event.event.flags = EV_ADD;
event.event.udata = udata;
if (eventKind == VIDEO_OUT_EVENT_SET_MODE) {
std::unique_lock lock(cfg->mutex);
event.triggered = true;
event.event.fflags = 1;
event.event.data = static_cast<intptr_t>(cfg->outputMode);
}
event.filter.triggerFunc = [](KernelEqueueEvent* e, void* data) {
const uint64_t old = static_cast<uint64_t>(e->event.data);
uint64_t counter = (old >> 12u) & 0xfu;
if (counter != 0xfu) {
counter++;
}
const uint64_t tsc = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()
).count()
) & 0xfffu;
const uint64_t payload = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
const uint64_t newData = tsc | (counter << 12u) | ((payload & 0x0000ffffffffffffULL) << 16u);
KernelEvent triggered = e->event;
triggered.fflags = triggered.fflags < 0xfu ? triggered.fflags + 1u : triggered.fflags;
triggered.data = static_cast<intptr_t>(newData);
if (e->triggered) {
e->pendingEvents.push_back(triggered);
} else {
e->event = triggered;
e->triggered = true;
}
};
event.filter.resetFunc = [](KernelEqueueEvent* e) {
e->triggered = false;
e->event.fflags = 0;
e->event.data = 0;
};
const int result = EqueueAddEvent(eq, event);
return result == EQUEUE_ERROR_EBADF ? VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE : result;
}
static int DeleteVideoOutEvent(int handle, KernelEqueue eq, int16_t eventKind) {
if (!VideoOutDriver::Get().IsOpen(handle)) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (eq == 0 || !EqueuePin(eq)) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
const int result = EqueueDeleteEvent(eq, static_cast<uintptr_t>(eventKind), EVFILT_VIDEO_OUT);
if (result == EQUEUE_ERROR_EBADF) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
return (result == EQUEUE_ERROR_ENOENT) ? 0 : result;
}
extern "C" {
int APS5_VABI sceVideoOutAddFlipEvent(KernelEqueue eq, int handle, void* udata) {
(void)eq;
(void)handle;
(void)udata;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutAddOutputModeEvent(KernelEqueue eq, int handle, void* udata) {
(void)eq;
(void)handle;
(void)udata;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutAddPreVblankStartEvent(KernelEqueue eq, int handle, void* udata) {
(void)eq;
(void)handle;
(void)udata;
NotImplemented_nid_no_patch(__func__);
return 0;
return RegisterVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_FLIP, udata);
}
int APS5_VABI sceVideoOutAddVblankEvent(KernelEqueue eq, int handle, void* udata) {
(void)eq;
(void)handle;
(void)udata;
NotImplemented_nid_no_patch(__func__);
return 0;
return RegisterVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_VBLANK, udata);
}
int APS5_VABI sceVideoOutAddPreVblankStartEvent(KernelEqueue eq, int handle, void* udata) {
return RegisterVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_PRE_VBLANK_START, udata);
}
int APS5_VABI sceVideoOutAddOutputModeEvent(KernelEqueue eq, int handle, void* udata) {
return RegisterVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_SET_MODE, udata);
}
int APS5_VABI sceVideoOutDeleteFlipEvent(KernelEqueue eq, int handle) {
(void)eq;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutDeletePreVblankStartEvent(KernelEqueue eq, int handle) {
(void)eq;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
return DeleteVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_FLIP);
}
int APS5_VABI sceVideoOutDeleteVblankEvent(KernelEqueue eq, int handle) {
(void)eq;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return DeleteVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_VBLANK);
}
int APS5_VABI sceVideoOutDeletePreVblankStartEvent(KernelEqueue eq, int handle) {
return DeleteVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_PRE_VBLANK_START);
}
int APS5_VABI sceVideoOutGetEventId(const KernelEvent* ev) {
if (ev == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
if (ev->filter != EVFILT_VIDEO_OUT) {
return VIDEO_OUT_ERROR_INVALID_EVENT;
}
const int ident = static_cast<int>(ev->ident);
if (ident != VIDEO_OUT_EVENT_FLIP && ident != VIDEO_OUT_EVENT_VBLANK && ident != VIDEO_OUT_EVENT_PRE_VBLANK_START && ident != VIDEO_OUT_EVENT_SET_MODE) {
return VIDEO_OUT_ERROR_INVALID_EVENT;
}
return ident;
}
int APS5_VABI sceVideoOutGetEventData(const KernelEvent* ev, int64_t* data) {
if (ev == nullptr || data == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
if (ev->filter != EVFILT_VIDEO_OUT) {
return VIDEO_OUT_ERROR_INVALID_EVENT;
}
uint64_t eventData = static_cast<uint64_t>(ev->data) >> 16u;
if (ev->ident == static_cast<uintptr_t>(VIDEO_OUT_EVENT_FLIP) && (static_cast<uint64_t>(ev->data) & 0x8000000000000000ULL) != 0) {
eventData |= 0xffff000000000000ULL;
}
*data = static_cast<int64_t>(eventData);
return 0;
}
int APS5_VABI sceVideoOutGetEventCount(const KernelEvent* ev) {
(void)ev;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutGetEventData(const KernelEvent* ev, int64_t* data) {
(void)ev;
(void)data;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutGetEventId(const KernelEvent* ev) {
(void)ev;
NotImplemented_nid_no_patch(__func__);
return 0;
if (ev == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
if (ev->filter != EVFILT_VIDEO_OUT) {
return VIDEO_OUT_ERROR_INVALID_EVENT;
}
return static_cast<int>((static_cast<uint64_t>(ev->data) >> 12u) & 0xfu);
}
}

View File

@ -1,96 +1,213 @@
#include <cstdint>
#include <cstring>
#include <mutex>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "../include/Output.hpp"
#include "prx/libSceVideoOut/include/Output.hpp"
#include "prx/libSceVideoOut/include/VideoOutDriver.hpp"
static int ValidateOutputConfig(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reservedPtr, uint64_t reserved) {
if (!VideoOutDriver::Get().IsOpen(handle)) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (reservedPtr != nullptr || reserved != 0) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (options != nullptr) {
for (auto v : options->internalData) {
if (v != 0) {
return VIDEO_OUT_ERROR_INVALID_OPTION;
}
}
}
if (mode != VIDEO_OUT_OUTPUT_MODE_DEFAULT && mode != VIDEO_OUT_OUTPUT_MODE_119_88HZ) {
return VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE;
}
return 0;
}
extern "C" {
int APS5_VABI sceVideoOutOpen(int user_id, int bus_type, int index, const void* param) {
(void)user_id;
(void)bus_type;
(void)index;
(void)param;
NotImplemented_nid_no_patch(__func__);
return 0;
if (user_id != 255 && user_id != 0) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (bus_type != VIDEO_OUT_BUS_TYPE_MAIN && bus_type != VIDEO_OUT_BUS_TYPE_OVERLAY && bus_type != VIDEO_OUT_BUS_TYPE_SUB) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (index != 0) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
const int handle = VideoOutDriver::Get().Open(bus_type);
if (handle < 0) {
return VIDEO_OUT_ERROR_RESOURCE_BUSY;
}
return handle;
}
int APS5_VABI sceVideoOutClose(int handle) {
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutConfigureOutput(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reserved_ptr, uint64_t reserved) {
(void)handle;
(void)mode;
(void)options;
(void)reserved_ptr;
(void)reserved;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutIsOutputSupported(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reserved_ptr, uint64_t reserved) {
(void)handle;
(void)mode;
(void)options;
(void)reserved_ptr;
(void)reserved;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutInitializeOutputOptions(VideoOutOutputOptions* options) {
(void)options;
NotImplemented_nid_no_patch(__func__);
return 0;
return VideoOutDriver::Get().Close(handle) ? 0 : VIDEO_OUT_ERROR_INVALID_HANDLE;
}
int APS5_VABI sceVideoOutSetFlipRate(int handle, int rate) {
(void)handle;
(void)rate;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutSetWindowModeMargins(int handle, int top, int bottom) {
(void)handle;
(void)top;
(void)bottom;
NotImplemented_nid_no_patch(__func__);
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (rate < 0 || rate > 2) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
std::unique_lock lock(cfg->mutex);
cfg->flipRate = rate;
return 0;
}
int APS5_VABI sceVideoOutGetFlipStatus(int handle, VideoOutFlipStatus* status) {
(void)handle;
(void)status;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVideoOutGetOutputStatus(int handle, VideoOutOutputStatus* status) {
(void)handle;
(void)status;
NotImplemented_nid_no_patch(__func__);
if (status == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
*status = cfg->flipStatus;
return 0;
}
int APS5_VABI sceVideoOutGetVblankStatus(int handle, VideoOutVblankStatus* status) {
(void)handle;
(void)status;
NotImplemented_nid_no_patch(__func__);
if (status == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
*status = cfg->vblankStatus;
return 0;
}
int APS5_VABI sceVideoOutGetOutputStatus(int handle, VideoOutOutputStatus* status) {
if (status == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
status->resolution = (cfg->width >= 3840 || cfg->height >= 2160) ? 2u : 1u;
status->dynamicRange = 1;
status->refreshRate = VIDEO_OUT_REFRESH_RATE_59_94HZ;
status->flags = 0;
status->reserved[0] = 0;
status->reserved[1] = 0;
status->reserved[2] = 0;
return 0;
}
int APS5_VABI sceVideoOutIsFlipPending(int handle) {
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
return cfg->flipStatus.flipPendingNum;
}
int APS5_VABI sceVideoOutWaitVblank(int handle) {
(void)handle;
NotImplemented_nid_no_patch(__func__);
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
const uint64_t count = cfg->vblankStatus.count;
while (cfg->opened && cfg->vblankStatus.count == count) {
cfg->vblankCond.wait(lock);
}
return 0;
}
int APS5_VABI sceVideoOutInitializeOutputOptions(VideoOutOutputOptions* options) {
if (options == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
std::memset(options, 0, sizeof(VideoOutOutputOptions));
return 0;
}
int APS5_VABI sceVideoOutIsOutputSupported(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reserved_ptr, uint64_t reserved) {
const int result = ValidateOutputConfig(handle, mode, options, reserved_ptr, reserved);
if (result != 0) {
return result;
}
return (mode == VIDEO_OUT_OUTPUT_MODE_119_88HZ) ? 0 : 1;
}
int APS5_VABI sceVideoOutConfigureOutput(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reserved_ptr, uint64_t reserved) {
const int supported = sceVideoOutIsOutputSupported(handle, mode, options, reserved_ptr, reserved);
if (supported < 0) {
return supported;
}
if (supported == 0 && mode == VIDEO_OUT_OUTPUT_MODE_119_88HZ) {
return VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE;
}
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
cfg->outputMode = mode;
return 0;
}
int APS5_VABI sceVideoOutSetWindowModeMargins(int handle, int top, int bottom) {
(void)top;
(void)bottom;
if (!VideoOutDriver::Get().IsOpen(handle)) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
return 0;
}
int APS5_VABI sceVideoOutLatencyControlWaitBeforeInput(int handle) {
if (!VideoOutDriver::Get().IsOpen(handle)) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
return 0;
}
int APS5_VABI sceVideoOutLatencyMeasureSetStartPoint(int handle, uint32_t point) {
(void)point;
if (!VideoOutDriver::Get().IsOpen(handle)) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
return 0;
}
int APS5_VABI sceVideoOutColorSettingsSetGamma(VideoOutColorSettings* settings, float gamma) {
if (settings == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
if (gamma < 0.1f || gamma > 2.0f) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
settings->gamma = gamma;
return 0;
}
int APS5_VABI sceVideoOutAdjustColor(int handle, const VideoOutColorSettings* settings) {
if (settings == nullptr) {
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
auto* cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock(cfg->mutex);
cfg->gamma = settings->gamma;
return 0;
}

View File

@ -0,0 +1,80 @@
#include "prx/libSceVideoOut/include/VideoOutDriver.hpp"
#include <stdexcept>
VideoOutDriver& VideoOutDriver::Get() {
static VideoOutDriver instance;
return instance;
}
int VideoOutDriver::Open(int busType) {
std::unique_lock lock(mutex);
const int handle = busType + 1;
if (handle <= 0 || handle >= VIDEO_OUT_NUM_MAX) {
return -1;
}
auto& cfg = contexts[handle];
std::unique_lock cfgLock(cfg.mutex);
if (cfg.opened) {
return -1;
}
cfg.closing = false;
cfg.opened = true;
cfg.flipRate = 0;
cfg.outputMode = VIDEO_OUT_OUTPUT_MODE_DEFAULT;
cfg.gamma = 1.0f;
cfg.flipStatus = VideoOutFlipStatus{};
cfg.flipStatus.flipArg = -1;
cfg.flipStatus.currentBuffer = -1;
cfg.vblankStatus = VideoOutVblankStatus{};
cfg.preVblankStatus = VideoOutVblankStatus{};
for (auto& buf : cfg.buffers) {
buf = VideoOutBuffer{};
}
for (auto& grp : cfg.groups) {
grp = BufferAttributeGroup{};
}
if (++cfg.generation == 0) {
throw std::runtime_error("VideoOut port generation wrapped");
}
return handle;
}
bool VideoOutDriver::Close(int handle) {
std::unique_lock lock(mutex);
if (handle <= 0 || handle >= VIDEO_OUT_NUM_MAX) {
return false;
}
auto& cfg = contexts[handle];
std::unique_lock cfgLock(cfg.mutex);
if (!cfg.opened || cfg.closing) {
return false;
}
cfg.opened = false;
cfg.closing = true;
if (++cfg.generation == 0) {
throw std::runtime_error("VideoOut port generation wrapped");
}
for (auto& buf : cfg.buffers) {
buf = VideoOutBuffer{};
}
for (auto& grp : cfg.groups) {
grp = BufferAttributeGroup{};
}
cfg.flipRate = 0;
cfg.vblankCond.notify_all();
return true;
}
VideoOutConfig* VideoOutDriver::GetConfig(int handle) {
std::unique_lock lock(mutex);
if (handle <= 0 || handle >= VIDEO_OUT_NUM_MAX || !contexts[handle].opened) {
return nullptr;
}
return &contexts[handle];
}
bool VideoOutDriver::IsOpen(int handle) {
std::unique_lock lock(mutex);
return handle > 0 && handle < VIDEO_OUT_NUM_MAX && contexts[handle].opened;
}

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef CORE_LIBS_PRX_LIBKERNEL_EQUEUE_EQUEUE_HPP
#define CORE_LIBS_PRX_LIBKERNEL_EQUEUE_EQUEUE_HPP
#include <condition_variable>
#include <cstdint>
@ -87,3 +88,5 @@ KernelEqueueRef EqueuePin(KernelEqueue eq);
int APS5_VABI EqueueAddEvent(KernelEqueue eq, const KernelEqueueEvent& event);
int APS5_VABI EqueueTriggerEvent(KernelEqueue eq, uintptr_t ident, int16_t filter, void* triggerData);
int APS5_VABI EqueueDeleteEvent(KernelEqueue eq, uintptr_t ident, int16_t filter);
#endif