diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 9fa85e725..94ef4354f 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -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 (*)(); diff --git a/core/libs/prx/libSceVideoOut/CMakeLists.txt b/core/libs/prx/libSceVideoOut/CMakeLists.txt index 087ce8db3..2d0a58942 100644 --- a/core/libs/prx/libSceVideoOut/CMakeLists.txt +++ b/core/libs/prx/libSceVideoOut/CMakeLists.txt @@ -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}) diff --git a/core/libs/prx/libSceVideoOut/include/Buffer.hpp b/core/libs/prx/libSceVideoOut/include/Buffer.hpp index 77e7d7b45..903e4029c 100644 --- a/core/libs/prx/libSceVideoOut/include/Buffer.hpp +++ b/core/libs/prx/libSceVideoOut/include/Buffer.hpp @@ -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 diff --git a/core/libs/prx/libSceVideoOut/include/Output.hpp b/core/libs/prx/libSceVideoOut/include/Output.hpp index d1fe0e1c6..915ffe34b 100644 --- a/core/libs/prx/libSceVideoOut/include/Output.hpp +++ b/core/libs/prx/libSceVideoOut/include/Output.hpp @@ -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 diff --git a/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp b/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp new file mode 100644 index 000000000..8d08a3d36 --- /dev/null +++ b/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp @@ -0,0 +1,100 @@ +#ifndef CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_VIDEOOUTDRIVER_HPP +#define CORE_LIBS_PRX_LIBSCEVIDEOUOUT_INCLUDE_VIDEOOUTDRIVER_HPP + +#include +#include +#include +#include + +#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 buffers{}; + std::array 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 diff --git a/core/libs/prx/libSceVideoOut/src/Buffer.cpp b/core/libs/prx/libSceVideoOut/src/Buffer.cpp index 5df1d26bf..b7f8d845d 100644 --- a/core/libs/prx/libSceVideoOut/src/Buffer.cpp +++ b/core/libs/prx/libSceVideoOut/src/Buffer.cpp @@ -1,57 +1,145 @@ -#include -#include "SceTypes.hpp" +#include +#include +#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(buffers[i].data); + buf.metadataAddress = reinterpret_cast(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; } diff --git a/core/libs/prx/libSceVideoOut/src/Event.cpp b/core/libs/prx/libSceVideoOut/src/Event.cpp index 5e7e89796..7baefccee 100644 --- a/core/libs/prx/libSceVideoOut/src/Event.cpp +++ b/core/libs/prx/libSceVideoOut/src/Event.cpp @@ -1,79 +1,146 @@ +#include + #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(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(cfg->outputMode); + } + event.filter.triggerFunc = [](KernelEqueueEvent* e, void* data) { + const uint64_t old = static_cast(e->event.data); + uint64_t counter = (old >> 12u) & 0xfu; + if (counter != 0xfu) { + counter++; + } + const uint64_t tsc = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch() + ).count() + ) & 0xfffu; + const uint64_t payload = static_cast(reinterpret_cast(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(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(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(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(ev->data) >> 16u; + if (ev->ident == static_cast(VIDEO_OUT_EVENT_FLIP) && (static_cast(ev->data) & 0x8000000000000000ULL) != 0) { + eventData |= 0xffff000000000000ULL; + } + *data = static_cast(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((static_cast(ev->data) >> 12u) & 0xfu); } } diff --git a/core/libs/prx/libSceVideoOut/src/Output.cpp b/core/libs/prx/libSceVideoOut/src/Output.cpp index 9e0a88709..b3ad34597 100644 --- a/core/libs/prx/libSceVideoOut/src/Output.cpp +++ b/core/libs/prx/libSceVideoOut/src/Output.cpp @@ -1,96 +1,213 @@ -#include +#include +#include + #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; } diff --git a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp new file mode 100644 index 000000000..f3bb955d0 --- /dev/null +++ b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp @@ -0,0 +1,80 @@ +#include "prx/libSceVideoOut/include/VideoOutDriver.hpp" + +#include + +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; +} diff --git a/core/libs/prx/libkernel/Equeue/Equeue.hpp b/core/libs/prx/libkernel/Equeue/Equeue.hpp index 0568285d9..f85dd71da 100644 --- a/core/libs/prx/libkernel/Equeue/Equeue.hpp +++ b/core/libs/prx/libkernel/Equeue/Equeue.hpp @@ -1,4 +1,5 @@ -#pragma once +#ifndef CORE_LIBS_PRX_LIBKERNEL_EQUEUE_EQUEUE_HPP +#define CORE_LIBS_PRX_LIBKERNEL_EQUEUE_EQUEUE_HPP #include #include @@ -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