libraries: Add initial HLE JPEG encoder skeleton (#1607)

* libraries: Add initial HLE JPEG encoder skeleton

* jpegenc: Finish adding parameter validation.

* updated enums , added logging

* jpegenc: Clean up parameter validations.

* jpegenc: Fix missing log.

* externals: Update ffmpeg-core

---------

Co-authored-by: georgemoralis <giorgosmrls@gmail.com>
This commit is contained in:
squidbus 2024-11-30 11:43:12 -08:00 committed by GitHub
parent 2002e37ce9
commit 7153838c4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 317 additions and 2 deletions

View file

@ -354,6 +354,11 @@ set(PNG_LIB src/core/libraries/libpng/pngdec.cpp
src/core/libraries/libpng/pngdec.h
)
set(JPEG_LIB src/core/libraries/jpeg/jpeg_error.h
src/core/libraries/jpeg/jpegenc.cpp
src/core/libraries/jpeg/jpegenc.h
)
set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp
src/core/libraries/playgo/playgo.h
src/core/libraries/playgo/playgo_types.h
@ -536,6 +541,7 @@ set(CORE src/core/aerolib/stubs.cpp
${VIDEOOUT_LIB}
${NP_LIBS}
${PNG_LIB}
${JPEG_LIB}
${PLAYGO_LIB}
${RANDOM_LIB}
${USBD_LIB}

@ -1 +1 @@
Subproject commit e30b7d7fe228bfb3f6e41ce1040b44a15eb7d5e0
Subproject commit 27de97c826b6b40c255891c37ac046a25836a575

View file

@ -22,6 +22,12 @@ template <typename T>
return static_cast<T>(value - value % size);
}
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
return (value & (alignment - 1)) == 0;
}
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr bool Is16KBAligned(T value) {

View file

@ -105,6 +105,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Rtc) \
SUB(Lib, DiscMap) \
SUB(Lib, Png) \
SUB(Lib, Jpeg) \
SUB(Lib, PlayGo) \
SUB(Lib, Random) \
SUB(Lib, Usbd) \

View file

@ -72,6 +72,7 @@ enum class Class : u8 {
Lib_Rtc, ///< The LibSceRtc implementation.
Lib_DiscMap, ///< The LibSceDiscMap implementation.
Lib_Png, ///< The LibScePng implementation.
Lib_Jpeg, ///< The LibSceJpeg implementation.
Lib_PlayGo, ///< The LibScePlayGo implementation.
Lib_Random, ///< The libSceRandom implementation.
Lib_Usbd, ///< The LibSceUsbd implementation.

View file

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_ADDR = 0x80650101;
constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_SIZE = 0x80650102;
constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_PARAM = 0x80650103;
constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_HANDLE = 0x80650104;

View file

@ -0,0 +1,207 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <magic_enum.hpp>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "jpeg_error.h"
#include "jpegenc.h"
namespace Libraries::JpegEnc {
constexpr s32 ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE = 0x800;
constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION = 0xFFFF;
constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_PITCH = 0xFFFFFFF;
constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_SIZE = 0x7FFFFFFF;
static s32 ValidateJpegEncCreateParam(const OrbisJpegEncCreateParam* param) {
if (!param) {
return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR;
}
if (param->size != sizeof(OrbisJpegEncCreateParam)) {
return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE;
}
if (param->attr != ORBIS_JPEG_ENC_ATTRIBUTE_NONE) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
return ORBIS_OK;
}
static s32 ValidateJpegEncMemory(const void* memory, const u32 memory_size) {
if (!memory) {
return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR;
}
if (memory_size < ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE) {
return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE;
}
return ORBIS_OK;
}
static s32 ValidateJpegEncEncodeParam(const OrbisJpegEncEncodeParam* param) {
// Validate addresses
if (!param) {
return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR;
}
if (!param->image || (param->pixel_format != ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 &&
!Common::IsAligned(reinterpret_cast<VAddr>(param->image), 4))) {
return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR;
}
if (!param->jpeg) {
return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR;
}
// Validate sizes
if (param->image_size == 0 || param->jpeg_size == 0) {
return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE;
}
// Validate parameters
if (param->image_width > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION ||
param->image_height > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
if (param->image_pitch == 0 || param->image_pitch > ORBIS_JPEG_ENC_MAX_IMAGE_PITCH ||
(param->pixel_format != ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 &&
!Common::IsAligned(param->image_pitch, 4))) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
const auto calculated_size = param->image_height * param->image_pitch;
if (calculated_size > ORBIS_JPEG_ENC_MAX_IMAGE_SIZE || calculated_size > param->image_size) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
if (param->encode_mode != ORBIS_JPEG_ENC_ENCODE_MODE_NORMAL &&
param->encode_mode != ORBIS_JPEG_ENC_ENCODE_MODE_MJPEG) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
if (param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC &&
param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
if (param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL &&
param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_422 &&
param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_420) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
if (param->restart_interval > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
switch (param->pixel_format) {
case ORBIS_JPEG_ENC_PIXEL_FORMAT_R8G8B8A8:
case ORBIS_JPEG_ENC_PIXEL_FORMAT_B8G8R8A8:
if (param->image_pitch >> 2 < param->image_width ||
param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC ||
param->sampling_type == ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
break;
case ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8U8Y8V8:
if (param->image_pitch >> 1 < Common::AlignUp(param->image_width, 2) ||
param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC ||
param->sampling_type == ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
break;
case ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8:
if (param->image_pitch < param->image_width ||
param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE ||
param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) {
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
break;
default:
return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM;
}
return ORBIS_OK;
}
static s32 ValidateJpecEngHandle(OrbisJpegEncHandle handle) {
if (!handle || !Common::IsAligned(reinterpret_cast<VAddr>(handle), 0x20) ||
handle->handle != handle) {
return ORBIS_JPEG_ENC_ERROR_INVALID_HANDLE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceJpegEncCreate(const OrbisJpegEncCreateParam* param, void* memory,
const u32 memory_size, OrbisJpegEncHandle* handle) {
if (auto param_ret = ValidateJpegEncCreateParam(param); param_ret != ORBIS_OK) {
LOG_ERROR(Lib_Jpeg, "Invalid create param");
return param_ret;
}
if (auto memory_ret = ValidateJpegEncMemory(memory, memory_size); memory_ret != ORBIS_OK) {
LOG_ERROR(Lib_Jpeg, "Invalid memory");
return memory_ret;
}
if (!handle) {
LOG_ERROR(Lib_Jpeg, "Invalid handle output");
return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR;
}
auto* handle_internal = reinterpret_cast<OrbisJpegEncHandleInternal*>(
Common::AlignUp(reinterpret_cast<VAddr>(memory), 0x20));
handle_internal->handle = handle_internal;
handle_internal->handle_size = sizeof(OrbisJpegEncHandleInternal*);
*handle = handle_internal;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceJpegEncDelete(OrbisJpegEncHandle handle) {
if (auto handle_ret = ValidateJpecEngHandle(handle); handle_ret != ORBIS_OK) {
LOG_ERROR(Lib_Jpeg, "Invalid handle");
return handle_ret;
}
handle->handle = nullptr;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceJpegEncEncode(OrbisJpegEncHandle handle, const OrbisJpegEncEncodeParam* param,
OrbisJpegEncOutputInfo* output_info) {
if (auto handle_ret = ValidateJpecEngHandle(handle); handle_ret != ORBIS_OK) {
LOG_ERROR(Lib_Jpeg, "Invalid handle");
return handle_ret;
}
if (auto param_ret = ValidateJpegEncEncodeParam(param); param_ret != ORBIS_OK) {
LOG_ERROR(Lib_Jpeg, "Invalid encode param");
return param_ret;
}
LOG_ERROR(Lib_Jpeg,
"(STUBBED) image_size = {} , jpeg_size = {} , image_width = {} , image_height = {} , "
"image_pitch = {} , pixel_format = {} , encode_mode = {} , color_space = {} , "
"sampling_type = {} , compression_ratio = {} , restart_interval = {}",
param->image_size, param->jpeg_size, param->image_width, param->image_height,
param->image_pitch, magic_enum::enum_name(param->pixel_format),
magic_enum::enum_name(param->encode_mode), magic_enum::enum_name(param->color_space),
magic_enum::enum_name(param->sampling_type), param->compression_ratio,
param->restart_interval);
if (output_info) {
output_info->size = param->jpeg_size;
output_info->height = param->image_height;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceJpegEncQueryMemorySize(const OrbisJpegEncCreateParam* param) {
if (auto param_ret = ValidateJpegEncCreateParam(param); param_ret != ORBIS_OK) {
LOG_ERROR(Lib_Jpeg, "Invalid create param");
return param_ret;
}
return ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE;
}
void RegisterlibSceJpegEnc(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("K+rocojkr-I", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncCreate);
LIB_FUNCTION("j1LyMdaM+C0", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncDelete);
LIB_FUNCTION("QbrU0cUghEM", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncEncode);
LIB_FUNCTION("o6ZgXfFdWXQ", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1,
sceJpegEncQueryMemorySize);
};
} // namespace Libraries::JpegEnc

View file

@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::JpegEnc {
enum OrbisJpegEncCreateParamAttributes : u32 { ORBIS_JPEG_ENC_ATTRIBUTE_NONE = 0 };
enum OrbisJpegEncEncodeParamPixelFormat : u16 {
ORBIS_JPEG_ENC_PIXEL_FORMAT_R8G8B8A8 = 0,
ORBIS_JPEG_ENC_PIXEL_FORMAT_B8G8R8A8 = 1,
ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8U8Y8V8 = 10,
ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 = 11
};
enum OrbisJpengEncEncodeParamEncodeMode : u16 {
ORBIS_JPEG_ENC_ENCODE_MODE_NORMAL = 0,
ORBIS_JPEG_ENC_ENCODE_MODE_MJPEG = 1
};
enum OrbisJpengEncEncodeParamColorSpace : u16 {
ORBIS_JPEG_ENC_COLOR_SPACE_YCC = 1,
ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE = 2
};
enum OrbisJpengEncEncodeParamSamplingType : u8 {
ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL = 0,
ORBIS_JPEG_ENC_SAMPLING_TYPE_422 = 1,
ORBIS_JPEG_ENC_SAMPLING_TYPE_420 = 2
};
struct OrbisJpegEncHandleInternal {
OrbisJpegEncHandleInternal* handle;
u32 handle_size;
};
static_assert(sizeof(OrbisJpegEncHandleInternal) == 0x10);
typedef OrbisJpegEncHandleInternal* OrbisJpegEncHandle;
struct OrbisJpegEncCreateParam {
u32 size;
OrbisJpegEncCreateParamAttributes attr;
};
static_assert(sizeof(OrbisJpegEncCreateParam) == 0x8);
struct OrbisJpegEncEncodeParam {
void* image;
void* jpeg;
u32 image_size;
u32 jpeg_size;
u32 image_width;
u32 image_height;
u32 image_pitch;
OrbisJpegEncEncodeParamPixelFormat pixel_format;
OrbisJpengEncEncodeParamEncodeMode encode_mode;
OrbisJpengEncEncodeParamColorSpace color_space;
OrbisJpengEncEncodeParamSamplingType sampling_type;
u8 compression_ratio;
s32 restart_interval;
};
static_assert(sizeof(OrbisJpegEncEncodeParam) == 0x30);
struct OrbisJpegEncOutputInfo {
u32 size;
u32 height;
};
static_assert(sizeof(OrbisJpegEncOutputInfo) == 0x8);
s32 PS4_SYSV_ABI sceJpegEncCreate(const OrbisJpegEncCreateParam* param, void* memory,
u32 memory_size, OrbisJpegEncHandle* handle);
s32 PS4_SYSV_ABI sceJpegEncDelete(OrbisJpegEncHandle handle);
s32 PS4_SYSV_ABI sceJpegEncEncode(OrbisJpegEncHandle handle, const OrbisJpegEncEncodeParam* param,
OrbisJpegEncOutputInfo* output_info);
s32 PS4_SYSV_ABI sceJpegEncQueryMemorySize(const OrbisJpegEncCreateParam* param);
void RegisterlibSceJpegEnc(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::JpegEnc

View file

@ -29,6 +29,7 @@
#include "core/file_sys/fs.h"
#include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/fiber/fiber.h"
#include "core/libraries/jpeg/jpegenc.h"
#include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libs.h"
#include "core/libraries/ngs2/ngs2.h"
@ -278,7 +279,7 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal},
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
{"libSceJpegEnc.sprx", nullptr},
{"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc},
{"libSceCesCs.sprx", nullptr}}};
std::vector<std::filesystem::path> found_modules;