hmd2: initialize reprojection and virtual headset field of view
Inspired by https://github.com/KytyPS5/KytyPS5/pull/607 Co-authored-by: BlackCAT <52720489+psnwd@users.noreply.github.com>
This commit is contained in:
parent
5610976720
commit
8e35e1233b
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef EMULATOR_SRC_LIBS_HMD2_H_
|
||||
#define EMULATOR_SRC_LIBS_HMD2_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Hmd2 {
|
||||
|
||||
inline constexpr uint32_t PANEL_WIDTH = 4000;
|
||||
inline constexpr uint32_t PANEL_HEIGHT = 2040;
|
||||
inline constexpr uint64_t OUTPUT_MODE_89_91HZ = 0x2000c;
|
||||
inline constexpr uint64_t OUTPUT_MODE_119_88HZ = 0x2000f;
|
||||
inline constexpr int32_t ERROR_REPROJECTION_NOT_INITIALIZED = -1972240373; // 0x8a72000b
|
||||
|
||||
struct ReprojectionState {
|
||||
std::atomic<uint64_t> output_mode {0};
|
||||
std::atomic<uint32_t> timing_us {3000};
|
||||
};
|
||||
|
||||
[[nodiscard]] ReprojectionState* GetReprojectionState();
|
||||
|
||||
} // namespace Libs::Hmd2
|
||||
|
||||
#endif // EMULATOR_SRC_LIBS_HMD2_H_
|
||||
|
|
@ -1,11 +1,16 @@
|
|||
#include "common/emulatorConfig.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "libs/agc.h"
|
||||
#include "libs/errno.h"
|
||||
#include "libs/hmd2.h"
|
||||
#include "libs/libs.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
|
||||
namespace Libs {
|
||||
|
||||
|
|
@ -16,6 +21,8 @@ namespace Hmd2 {
|
|||
constexpr int32_t ERROR_ALREADY_INITIALIZED = -1972240383; // 0x8a720001
|
||||
constexpr int32_t ERROR_NOT_INITIALIZED = -1972240382; // 0x8a720002
|
||||
constexpr int32_t ERROR_PARAMETER_NULL = -1972240376; // 0x8a720008
|
||||
constexpr int32_t ERROR_REPROJECTION_ALREADY_INITIALIZED = -1972240372; // 0x8a72000c
|
||||
constexpr int32_t ERROR_REPROJECTION_IN_VR_MODE = -1972240356; // 0x8a72001c
|
||||
|
||||
struct SceHmd2InitializeParam {
|
||||
void* reserved0;
|
||||
|
|
@ -41,14 +48,59 @@ struct SceHmd2DeviceInformation {
|
|||
float virtualImageDistance;
|
||||
};
|
||||
|
||||
struct SceHmd2FieldOfView {
|
||||
float tanOut;
|
||||
float tanIn;
|
||||
float tanTop;
|
||||
float tanBottom;
|
||||
};
|
||||
|
||||
struct SceHmd2ReprojectionInitializeParam {
|
||||
void* pReprojectionBuff;
|
||||
void* pDisplayBuff;
|
||||
int32_t threadPriority;
|
||||
int32_t cpuAffinityMask;
|
||||
uint32_t pipeId;
|
||||
uint32_t queueId;
|
||||
uint32_t type;
|
||||
void* pSeeThroughBuff;
|
||||
int32_t reprojectionTiming;
|
||||
uint32_t reserved[5];
|
||||
};
|
||||
|
||||
static_assert(sizeof(SceHmd2InitializeParam) == 16);
|
||||
static_assert(offsetof(SceHmd2InitializeParam, reserved) == 8);
|
||||
static_assert(sizeof(SceHmd2DeviceInformation) == 28);
|
||||
static_assert(offsetof(SceHmd2DeviceInformation, deviceInfo) == 8);
|
||||
static_assert(offsetof(SceHmd2DeviceInformation, hmuMount) == 20);
|
||||
static_assert(offsetof(SceHmd2DeviceInformation, virtualImageDistance) == 24);
|
||||
static_assert(sizeof(SceHmd2FieldOfView) == 16);
|
||||
static_assert(alignof(SceHmd2FieldOfView) == 4);
|
||||
static_assert(sizeof(SceHmd2ReprojectionInitializeParam) == 72);
|
||||
static_assert(alignof(SceHmd2ReprojectionInitializeParam) == 8);
|
||||
static_assert(offsetof(SceHmd2ReprojectionInitializeParam, threadPriority) == 16);
|
||||
static_assert(offsetof(SceHmd2ReprojectionInitializeParam, type) == 32);
|
||||
static_assert(offsetof(SceHmd2ReprojectionInitializeParam, pSeeThroughBuff) == 40);
|
||||
static_assert(offsetof(SceHmd2ReprojectionInitializeParam, reprojectionTiming) == 48);
|
||||
static_assert(offsetof(SceHmd2ReprojectionInitializeParam, reserved) == 52);
|
||||
|
||||
static std::atomic<bool> g_initialized = false;
|
||||
static std::mutex g_reprojection_mutex;
|
||||
static std::atomic<ReprojectionState*> g_reprojection_state {nullptr};
|
||||
|
||||
ReprojectionState* GetReprojectionState() {
|
||||
return g_reprojection_state.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
static Graphics::SizeAlign ReprojectionDisplaySize() {
|
||||
// The host display target is one uncompressed tiled 10-bit panel surface.
|
||||
Graphics::TileSizeAlign size {};
|
||||
Graphics::TileGetTextureTotalSize(Graphics::Prospero::BufferFormat::k10_10_10_2UNorm,
|
||||
PANEL_WIDTH, PANEL_HEIGHT, 1, 1,
|
||||
Graphics::Prospero::TileMode::kRenderTarget, false, size);
|
||||
EXIT_IF(size.size == 0 || size.align == 0);
|
||||
return {size.size, size.align};
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI Hmd2Initialize(const SceHmd2InitializeParam* param) {
|
||||
PRINT_NAME();
|
||||
|
|
@ -76,7 +128,7 @@ static int32_t KYTY_SYSV_ABI Hmd2GetDeviceInformation(SceHmd2DeviceInformation*
|
|||
info->hmuMount = 1;
|
||||
info->lensSeparationDistance = 63;
|
||||
// Headset characteristics: 4000x2040 panel and 2 m virtual image distance.
|
||||
info->deviceInfo.panelResolution = {4000, 2040};
|
||||
info->deviceInfo.panelResolution = {PANEL_WIDTH, PANEL_HEIGHT};
|
||||
info->virtualImageDistance = 2.0f;
|
||||
// Host presentation has no emulated headset scan-out delay; latency remains zero.
|
||||
} else {
|
||||
|
|
@ -88,11 +140,100 @@ static int32_t KYTY_SYSV_ABI Hmd2GetDeviceInformation(SceHmd2DeviceInformation*
|
|||
return OK;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI Hmd2GetFieldOfViewWithoutHandle(SceHmd2FieldOfView* fov) {
|
||||
PRINT_NAME();
|
||||
if (!g_initialized.load()) {
|
||||
return ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (fov == nullptr) {
|
||||
return ERROR_PARAMETER_NULL;
|
||||
}
|
||||
// Virtual ER15 field of view: 55/42.5/51.2/51.2 degrees.
|
||||
// These tangents describe that geometry, not bit-exact firmware output.
|
||||
*fov = {1.4281480312f, 0.9163311720f, 1.2437491417f, 1.2437491417f};
|
||||
LOGF("Hmd2: FOV tangents out=%f in=%f top=%f bottom=%f\n", fov->tanOut, fov->tanIn,
|
||||
fov->tanTop, fov->tanBottom);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static Graphics::SizeAlign KYTY_SYSV_ABI Hmd2ReprojectionQueryBufferSizeAlign() {
|
||||
const Graphics::SizeAlign size {sizeof(ReprojectionState), alignof(ReprojectionState)};
|
||||
LOGF("Hmd2: reprojection work size=%" PRIu64 " alignment=%zu\n", size.m_size, size.m_align);
|
||||
return size;
|
||||
}
|
||||
|
||||
static Graphics::SizeAlign KYTY_SYSV_ABI Hmd2ReprojectionQueryDisplayBufferSizeAlign() {
|
||||
const auto size = ReprojectionDisplaySize();
|
||||
LOGF("Hmd2: reprojection display size=%" PRIu64 " alignment=%zu\n", size.m_size,
|
||||
size.m_align);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI
|
||||
Hmd2ReprojectionInitialize(const SceHmd2ReprojectionInitializeParam* param, void*) {
|
||||
std::scoped_lock lock {g_reprojection_mutex};
|
||||
if (GetReprojectionState() != nullptr) {
|
||||
return ERROR_REPROJECTION_ALREADY_INITIALIZED;
|
||||
}
|
||||
if (param == nullptr || param->pReprojectionBuff == nullptr || param->pDisplayBuff == nullptr) {
|
||||
return ERROR_PARAMETER_NULL;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(param->pSeeThroughBuff != nullptr);
|
||||
const auto display_size = ReprojectionDisplaySize();
|
||||
auto* state = new (param->pReprojectionBuff) ReprojectionState;
|
||||
if (param->reprojectionTiming != 0) {
|
||||
state->timing_us.store(static_cast<uint32_t>(param->reprojectionTiming),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
g_reprojection_state.store(state, std::memory_order_release);
|
||||
LOGF("Hmd2: reprojection initialized work=%p display=%p size=%" PRIu64 " timing=%u\n",
|
||||
static_cast<void*>(state), param->pDisplayBuff, display_size.m_size,
|
||||
state->timing_us.load(std::memory_order_relaxed));
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI Hmd2ReprojectionEnableVrMode(uint64_t output_mode) {
|
||||
auto* state = GetReprojectionState();
|
||||
if (state == nullptr) {
|
||||
return ERROR_REPROJECTION_NOT_INITIALIZED;
|
||||
}
|
||||
if (output_mode != OUTPUT_MODE_89_91HZ && output_mode != OUTPUT_MODE_119_88HZ) {
|
||||
return VideoOut::VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||
}
|
||||
if (!Config::VrEnabled()) {
|
||||
return VideoOut::VIDEO_OUT_ERROR_NO_DEVICE;
|
||||
}
|
||||
uint64_t expected = 0;
|
||||
if (!state->output_mode.compare_exchange_strong(expected, output_mode)) {
|
||||
return ERROR_REPROJECTION_IN_VR_MODE;
|
||||
}
|
||||
LOGF("Hmd2: VR output mode=0x%" PRIx64 "\n", output_mode);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI Hmd2ReprojectionSetTiming(int32_t timing) {
|
||||
auto* state = GetReprojectionState();
|
||||
if (state == nullptr) {
|
||||
return ERROR_REPROJECTION_NOT_INITIALIZED;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(timing < 2000 || timing > 7000);
|
||||
const auto rounded = static_cast<uint32_t>((timing + 99) / 100 * 100);
|
||||
state->timing_us.store(rounded, std::memory_order_release);
|
||||
LOGF("Hmd2: reprojection timing=%u us\n", rounded);
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Hmd2
|
||||
|
||||
LIB_DEFINE(InitHmd2_1) {
|
||||
LIB_FUNC("c812oYs7Vsc", Hmd2::Hmd2Initialize);
|
||||
LIB_FUNC("bIi4YUfSRys", Hmd2::Hmd2GetDeviceInformation);
|
||||
LIB_FUNC("gF8+lvc7GuQ", Hmd2::Hmd2GetFieldOfViewWithoutHandle);
|
||||
LIB_FUNC("U-CnbmeyYaA", Hmd2::Hmd2ReprojectionQueryBufferSizeAlign);
|
||||
LIB_FUNC("-C2nkoEYOnU", Hmd2::Hmd2ReprojectionQueryDisplayBufferSizeAlign);
|
||||
LIB_FUNC("C0rPwER-yxg", Hmd2::Hmd2ReprojectionInitialize);
|
||||
LIB_FUNC("VVvFh51o20s", Hmd2::Hmd2ReprojectionEnableVrMode);
|
||||
LIB_FUNC("FkQX7rjFomk", Hmd2::Hmd2ReprojectionSetTiming);
|
||||
}
|
||||
|
||||
} // namespace Libs
|
||||
|
|
|
|||
Loading…
Reference in New Issue