Files
cocos-engine/native/cocos/platform/java/modules/XRInterface.cpp
oahc09 70b81a9d8c [feature] xr remote preview (#13247)
* feat:add xr remote preview feature

send xr device's pose info to web browser or editor

* feat:add xr remote preview feature

* fix:clang tidy error
2022-11-09 15:02:12 +08:00

994 lines
37 KiB
C++

/****************************************************************************
Copyright (c) 2021-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "XRInterface.h"
#include <fcntl.h>
#include <unistd.h>
#include <functional>
#include <unordered_map>
#include "base/StringUtil.h"
#include "android/AndroidPlatform.h"
#include "base/Log.h"
#include "base/Macros.h"
#include "bindings/event/EventDispatcher.h"
#include "cocos/bindings/jswrapper/SeApi.h"
#include "java/jni/JniHelper.h"
#include "renderer/GFXDeviceManager.h"
#ifdef CC_USE_VULKAN
#include "gfx-vulkan/VKDevice.h"
#endif
#ifdef CC_USE_GLES3
#include "gfx-gles-common/gles3w.h"
#include "gfx-gles3/GLES3Device.h"
#include "renderer/gfx-gles3/GLES3GPUObjects.h"
#endif
#if CC_USE_XR
#include "Xr.h"
#endif
#include "application/ApplicationManager.h"
#include "base/threading/MessageQueue.h"
#include "platform/Image.h"
// print log
const bool IS_ENABLE_XR_LOG = false;
namespace cc {
const static ccstd::unordered_map<xr::XRClick::Type, StickKeyCode> CLICK_TYPE_TO_KEY_CODE = {
{xr::XRClick::Type::MENU, StickKeyCode::MENU},
{xr::XRClick::Type::TRIGGER_LEFT, StickKeyCode::TRIGGER_LEFT},
{xr::XRClick::Type::SHOULDER_LEFT, StickKeyCode::L1},
{xr::XRClick::Type::THUMBSTICK_LEFT, StickKeyCode::L3},
{xr::XRClick::Type::X, StickKeyCode::Y},
{xr::XRClick::Type::Y, StickKeyCode::X},
{xr::XRClick::Type::TRIGGER_RIGHT, StickKeyCode::TRIGGER_RIGHT},
{xr::XRClick::Type::SHOULDER_RIGHT, StickKeyCode::R1},
{xr::XRClick::Type::THUMBSTICK_RIGHT, StickKeyCode::R3},
{xr::XRClick::Type::A, StickKeyCode::B},
{xr::XRClick::Type::B, StickKeyCode::A},
{xr::XRClick::Type::HOME, StickKeyCode::UNDEFINE},
{xr::XRClick::Type::START, StickKeyCode::START},
{xr::XRClick::Type::DPAD_DOWN, StickKeyCode::Y},
{xr::XRClick::Type::DPAD_UP, StickKeyCode::Y},
{xr::XRClick::Type::DPAD_LEFT, StickKeyCode::X},
{xr::XRClick::Type::DPAD_RIGHT, StickKeyCode::X}};
const static ccstd::unordered_map<xr::XRGrab::Type, StickAxisCode> GRAB_TYPE_TO_AXIS_CODE = {
{xr::XRGrab::Type::TRIGGER_LEFT, StickAxisCode::L2},
{xr::XRGrab::Type::TRIGGER_RIGHT, StickAxisCode::R2},
{xr::XRGrab::Type::GRIP_LEFT, StickAxisCode::LEFT_GRIP},
{xr::XRGrab::Type::GRIP_RIGHT, StickAxisCode::RIGHT_GRIP},
};
void XRInterface::dispatchGamepadEventInternal(const xr::XRControllerEvent &xrControllerEvent) {
if (xrControllerEvent.xrControllerInfos.empty()) {
return;
}
auto *controllerInfo = ccnew ControllerInfo();
if (!controllerInfo) {
return;
}
size_t length = xrControllerEvent.xrControllerInfos.size();
for (size_t i = 0; i < length; i++) {
switch (xrControllerEvent.xrControllerInfos.at(i)->getXREventType()) {
case xr::XREventType::CLICK: {
auto *xrClick = static_cast<xr::XRClick *>(xrControllerEvent.xrControllerInfos.at(i).get());
StickKeyCode stickKeyCode = CLICK_TYPE_TO_KEY_CODE.at(xrClick->type);
switch (xrClick->type) {
case xr::XRClick::Type::MENU:
case xr::XRClick::Type::TRIGGER_LEFT:
case xr::XRClick::Type::SHOULDER_LEFT:
case xr::XRClick::Type::THUMBSTICK_LEFT:
case xr::XRClick::Type::X:
case xr::XRClick::Type::Y:
case xr::XRClick::Type::TRIGGER_RIGHT:
case xr::XRClick::Type::SHOULDER_RIGHT:
case xr::XRClick::Type::THUMBSTICK_RIGHT:
case xr::XRClick::Type::A:
case xr::XRClick::Type::B:
case xr::XRClick::Type::START: {
controllerInfo->buttonInfos.emplace_back(ControllerInfo::ButtonInfo(stickKeyCode, xrClick->isPress));
break;
}
case xr::XRClick::Type::HOME: {
CC_LOG_INFO("[XRInterface] exit when home click in rokid.");
#if CC_USE_XR
xr::XrEntry::getInstance()->destroyXrInstance();
xr::XrEntry::destroyInstance();
#endif
CC_CURRENT_APPLICATION_SAFE()->close();
break;
}
case xr::XRClick::Type::DPAD_UP:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::Y, xrClick->isPress ? 1.F : 0.F));
break;
case xr::XRClick::Type::DPAD_DOWN:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::Y, xrClick->isPress ? -1.F : 0.F));
break;
case xr::XRClick::Type::DPAD_LEFT:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::X, xrClick->isPress ? -1.F : 0.F));
break;
case xr::XRClick::Type::DPAD_RIGHT:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::X, xrClick->isPress ? 1.F : 0.F));
break;
default:
break;
}
} break;
case xr::XREventType::STICK: {
auto *xrStick = static_cast<xr::XRStick *>(xrControllerEvent.xrControllerInfos.at(i).get());
switch (xrStick->type) {
case xr::XRStick::Type::STICK_LEFT:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_X, xrStick->x));
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_Y, xrStick->y));
break;
case xr::XRStick::Type::STICK_RIGHT:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_X, xrStick->x));
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_Y, xrStick->y));
break;
default:
break;
}
} break;
case xr::XREventType::GRAB: {
auto *xrGrab = static_cast<xr::XRGrab *>(xrControllerEvent.xrControllerInfos.at(i).get());
StickAxisCode stickAxisCode = GRAB_TYPE_TO_AXIS_CODE.at(xrGrab->type);
switch (xrGrab->type) {
case xr::XRGrab::Type::TRIGGER_LEFT:
case xr::XRGrab::Type::TRIGGER_RIGHT: {
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(stickAxisCode, xrGrab->value));
break;
}
default:
break;
}
} break;
default:
break;
}
}
controllerInfo->napdId = 0; // xr only one gamepad connection
_controllerEvent.controllerInfos.emplace_back(controllerInfo);
_controllerEvent.type = ControllerEvent::Type::GAMEPAD;
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
if (!controllerInfo->buttonInfos.empty()) {
for (const auto &btnInfo : controllerInfo->buttonInfos) {
_xrRemotePreviewManager->sendControllerKeyInfo(btnInfo);
}
}
if (!controllerInfo->axisInfos.empty()) {
for (const auto &axisInfo : controllerInfo->axisInfos) {
_xrRemotePreviewManager->sendControllerKeyInfo(axisInfo);
}
}
}
#endif
events::Controller::broadcast(_controllerEvent);
_controllerEvent.type = ControllerEvent::Type::UNKNOWN;
_controllerEvent.controllerInfos.clear();
}
void XRInterface::dispatchHandleEventInternal(const xr::XRControllerEvent &xrControllerEvent) {
if (xrControllerEvent.xrControllerInfos.empty()) {
return;
}
auto *controllerInfo = ccnew ControllerInfo();
if (!controllerInfo) {
return;
}
size_t length = xrControllerEvent.xrControllerInfos.size();
se::AutoHandleScope scope;
uint32_t poseIndex = 0;
for (size_t i = 0; i < length; i++) {
switch (xrControllerEvent.xrControllerInfos.at(i)->getXREventType()) {
case xr::XREventType::CLICK: {
auto *xrClick = static_cast<xr::XRClick *>(xrControllerEvent.xrControllerInfos.at(i).get());
StickKeyCode stickKeyCode = CLICK_TYPE_TO_KEY_CODE.at(xrClick->type);
switch (xrClick->type) {
case xr::XRClick::Type::MENU: {
#if !XR_OEM_SEED
controllerInfo->buttonInfos.emplace_back(ControllerInfo::ButtonInfo(StickKeyCode::MENU, xrClick->isPress));
#else
CC_LOG_INFO("[XRInterface] exit when menu click in seed.");
CC_CURRENT_APPLICATION_SAFE()->close();
#endif
break;
}
case xr::XRClick::Type::TRIGGER_LEFT:
case xr::XRClick::Type::THUMBSTICK_LEFT:
case xr::XRClick::Type::X:
case xr::XRClick::Type::Y:
case xr::XRClick::Type::TRIGGER_RIGHT:
case xr::XRClick::Type::THUMBSTICK_RIGHT:
case xr::XRClick::Type::A:
case xr::XRClick::Type::B: {
controllerInfo->buttonInfos.emplace_back(ControllerInfo::ButtonInfo(stickKeyCode, xrClick->isPress));
break;
}
default:
break;
}
} break;
case xr::XREventType::STICK: {
auto *xrStick = static_cast<xr::XRStick *>(xrControllerEvent.xrControllerInfos.at(i).get());
switch (xrStick->type) {
case xr::XRStick::Type::STICK_LEFT:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_X, xrStick->x));
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_Y, xrStick->y));
break;
case xr::XRStick::Type::STICK_RIGHT:
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_X, xrStick->x));
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_Y, xrStick->y));
break;
default:
break;
}
} break;
case xr::XREventType::GRAB: {
auto *xrGrab = static_cast<xr::XRGrab *>(xrControllerEvent.xrControllerInfos.at(i).get());
StickAxisCode stickAxisCode = GRAB_TYPE_TO_AXIS_CODE.at(xrGrab->type);
switch (xrGrab->type) {
case xr::XRGrab::Type::TRIGGER_LEFT:
case xr::XRGrab::Type::TRIGGER_RIGHT:
case xr::XRGrab::Type::GRIP_LEFT:
case xr::XRGrab::Type::GRIP_RIGHT: {
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(stickAxisCode, xrGrab->value));
break;
}
default:
break;
}
} break;
case xr::XREventType::POSE: {
auto *xrPose = static_cast<xr::XRPose *>(xrControllerEvent.xrControllerInfos.at(i).get());
switch (xrPose->type) {
case xr::XRPose::Type::HAND_LEFT:
case xr::XRPose::Type::HAND_RIGHT:
case xr::XRPose::Type::AIM_LEFT:
case xr::XRPose::Type::AIM_RIGHT: {
auto *jsPose = se::Object::createPlainObject();
jsPose->setProperty("code", se::Value(static_cast<int>(xrPose->type)));
jsPose->setProperty("x", se::Value(xrPose->px));
jsPose->setProperty("y", se::Value(xrPose->py));
jsPose->setProperty("z", se::Value(xrPose->pz));
jsPose->setProperty("quaternionX", se::Value(xrPose->qx));
jsPose->setProperty("quaternionY", se::Value(xrPose->qy));
jsPose->setProperty("quaternionZ", se::Value(xrPose->qz));
jsPose->setProperty("quaternionW", se::Value(xrPose->qw));
if (!_jsPoseEventArray) {
_jsPoseEventArray = se::Object::createArrayObject(0);
_jsPoseEventArray->root();
}
_jsPoseEventArray->setArrayElement(poseIndex, se::Value(jsPose));
poseIndex++;
} break;
default:
break;
}
} break;
default:
break;
}
}
if (poseIndex > 0) {
_jsPoseEventArray->setProperty("length", se::Value(poseIndex));
se::ValueArray args;
args.emplace_back(se::Value(_jsPoseEventArray));
EventDispatcher::doDispatchJsEvent("onHandlePoseInput", args);
}
if (!controllerInfo->buttonInfos.empty() || !controllerInfo->axisInfos.empty()) {
controllerInfo->napdId = 0; // xr only one handle connection
_controllerEvent.controllerInfos.emplace_back(controllerInfo);
_controllerEvent.type = ControllerEvent::Type::HANDLE;
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
if (!controllerInfo->buttonInfos.empty()) {
for (const auto &btnInfo : controllerInfo->buttonInfos) {
_xrRemotePreviewManager->sendControllerKeyInfo(btnInfo);
}
}
if (!controllerInfo->axisInfos.empty()) {
for (const auto &axisInfo : controllerInfo->axisInfos) {
_xrRemotePreviewManager->sendControllerKeyInfo(axisInfo);
}
}
}
#endif
events::Controller::broadcast(_controllerEvent);
_controllerEvent.type = ControllerEvent::Type::UNKNOWN;
_controllerEvent.controllerInfos.clear();
} else {
CC_SAFE_DELETE(controllerInfo)
}
}
void XRInterface::dispatchHMDEventInternal(const xr::XRControllerEvent &xrControllerEvent) {
if (xrControllerEvent.xrControllerInfos.empty()) {
return;
}
size_t length = xrControllerEvent.xrControllerInfos.size();
se::AutoHandleScope scope;
uint32_t poseIndex = 0;
for (size_t i = 0; i < length; i++) {
if (xrControllerEvent.xrControllerInfos.at(i)->getXREventType() == xr::XREventType::POSE) {
auto *xrPose = static_cast<xr::XRPose *>(xrControllerEvent.xrControllerInfos.at(i).get());
switch (xrPose->type) {
case xr::XRPose::Type::VIEW_LEFT:
case xr::XRPose::Type::VIEW_RIGHT:
case xr::XRPose::Type::HEAD_MIDDLE: {
auto *jsPose = se::Object::createPlainObject();
jsPose->setProperty("code", se::Value(static_cast<int>(xrPose->type)));
jsPose->setProperty("x", se::Value(xrPose->px));
jsPose->setProperty("y", se::Value(xrPose->py));
jsPose->setProperty("z", se::Value(xrPose->pz));
jsPose->setProperty("quaternionX", se::Value(xrPose->qx));
jsPose->setProperty("quaternionY", se::Value(xrPose->qy));
jsPose->setProperty("quaternionZ", se::Value(xrPose->qz));
jsPose->setProperty("quaternionW", se::Value(xrPose->qw));
if (!_jsPoseEventArray) {
_jsPoseEventArray = se::Object::createArrayObject(0);
_jsPoseEventArray->root();
}
_jsPoseEventArray->setArrayElement(poseIndex, se::Value(jsPose));
poseIndex++;
} break;
default:
break;
}
}
}
if (poseIndex > 0) {
_jsPoseEventArray->setProperty("length", se::Value(poseIndex));
se::ValueArray args;
args.emplace_back(se::Value(_jsPoseEventArray));
EventDispatcher::doDispatchJsEvent("onHMDPoseInput", args);
}
}
xr::XRVendor XRInterface::getVendor() {
#if CC_USE_XR
return static_cast<xr::XRVendor>(xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::DEVICE_VENDOR).getInt());
#else
return xr::XRVendor::MONADO;
#endif
}
xr::XRConfigValue XRInterface::getXRConfig(xr::XRConfigKey key) {
#if CC_USE_XR
return xr::XrEntry::getInstance()->getXRConfig(key);
#else
CC_UNUSED_PARAM(key);
cc::xr::XRConfigValue configValue;
return configValue;
#endif
}
void XRInterface::setXRConfig(xr::XRConfigKey key, xr::XRConfigValue value) {
#if CC_USE_XR
CC_LOG_INFO("[XR] setConfigParameterI %d", key);
xr::XrEntry::getInstance()->setXRConfig(key, value);
#else
CC_UNUSED_PARAM(key);
CC_UNUSED_PARAM(value);
#endif
}
uint32_t XRInterface::getRuntimeVersion() {
#if CC_USE_XR
return xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::RUNTIME_VERSION).getInt();
#else
return 1;
#endif
}
void XRInterface::initialize(void *javaVM, void *activity) {
#if CC_USE_XR
CC_LOG_INFO("[XR] initialize vm.%p,aty.%p | %d", javaVM, activity, (int)gettid());
xr::XrEntry::getInstance()->initPlatformData(javaVM, activity);
xr::XrEntry::getInstance()->setGamepadCallback(std::bind(&XRInterface::dispatchGamepadEventInternal, this, std::placeholders::_1));
xr::XrEntry::getInstance()->setHandleCallback(std::bind(&XRInterface::dispatchHandleEventInternal, this, std::placeholders::_1));
xr::XrEntry::getInstance()->setHMDCallback(std::bind(&XRInterface::dispatchHMDEventInternal, this, std::placeholders::_1));
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::LOGIC_THREAD_ID, static_cast<int>(gettid()));
xr::XrEntry::getInstance()->setXRConfigCallback([this](xr::XRConfigKey key, xr::XRConfigValue value) {
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("XRConfigCallback.%d", key);
if (key == xr::XRConfigKey::RENDER_EYE_FRAME_LEFT || key == xr::XRConfigKey::RENDER_EYE_FRAME_RIGHT) {
if (value.getInt() == 0) {
this->beginRenderEyeFrame(key == xr::XRConfigKey::RENDER_EYE_FRAME_LEFT ? 0 : 1);
}
if (value.getInt() == 1) {
this->endRenderEyeFrame(key == xr::XRConfigKey::RENDER_EYE_FRAME_LEFT ? 0 : 1);
}
} else if(key == xr::XRConfigKey::IMAGE_TRACKING_CANDIDATEIMAGE && value.isString()) {
if(!_gThreadPool) {
_gThreadPool = LegacyThreadPool::newSingleThreadPool();
}
std::string imageInfo = value.getString();
_gThreadPool->pushTask([imageInfo, this](int /*tid*/) {
this->loadAssetsImage(imageInfo);
});
}
});
#if XR_OEM_PICO
std::string graphicsApiName = GraphicsApiOpenglES;
#if CC_USE_VULKAN
graphicsApiName = GraphicsApiVulkan_1_0;
#endif
xr::XrEntry::getInstance()->createXrInstance(graphicsApiName.c_str());
#endif
#if CC_USE_XR_REMOTE_PREVIEW
_xrRemotePreviewManager = new XRRemotePreviewManager();
#endif
#else
CC_UNUSED_PARAM(javaVM);
CC_UNUSED_PARAM(activity);
#endif
}
// render thread lifecycle
void XRInterface::onRenderPause() {
#if CC_USE_XR
if (!_renderPaused) {
_renderPaused = true;
_renderResumed = false;
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
_xrRemotePreviewManager->pause();
}
#endif
CC_LOG_INFO("[XR] onRenderPause");
xr::XrEntry::getInstance()->pauseXrInstance();
}
#endif
}
void XRInterface::onRenderResume() {
#if CC_USE_XR
if (!_renderResumed) {
_renderResumed = true;
_renderPaused = false;
CC_LOG_INFO("[XR] onRenderResume");
xr::XrEntry::getInstance()->resumeXrInstance();
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
_xrRemotePreviewManager->resume();
}
#endif
}
#endif
}
void XRInterface::onRenderDestroy() {
#if CC_USE_XR
CC_LOG_INFO("[XR] onRenderDestroy");
xr::XrEntry::getInstance()->destroyXrInstance();
xr::XrEntry::destroyInstance();
if (_jsPoseEventArray != nullptr) {
_jsPoseEventArray->unroot();
_jsPoseEventArray->decRef();
_jsPoseEventArray = nullptr;
}
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
_xrRemotePreviewManager->stop();
}
#endif
#endif
}
// render thread lifecycle
// gfx
void XRInterface::preGFXDeviceInitialize(gfx::API gfxApi) {
#if CC_USE_XR
CC_LOG_INFO("[XR] preGFXDeviceInitialize.api.%d | Multi Thread.%d", gfxApi, gfx::DeviceAgent::getInstance() ? 1 : 0);
setXRConfig(xr::XRConfigKey::MULTITHREAD_MODE, gfx::DeviceAgent::getInstance() != nullptr);
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::RENDER_THREAD_ID, static_cast<int>(gettid()));
if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
#if !XR_OEM_PICO
std::string graphicsApiName = gfxApi == gfx::API::GLES3 ? GraphicsApiOpenglES : GraphicsApiVulkan_1_1;
xr::XrEntry::getInstance()->createXrInstance(graphicsApiName.c_str());
#endif
}
#else
CC_UNUSED_PARAM(gfxApi);
#endif
}
void XRInterface::postGFXDeviceInitialize(gfx::API gfxApi) {
#if CC_USE_XR
CC_LOG_INFO("[XR] postGFXDeviceInitialize.api.%d", gfxApi);
if (gfxApi == gfx::API::GLES3) {
#if CC_USE_GLES3
xr::XrEntry::getInstance()->initXrSession(_gles3wLoadFuncProc,
_gles3GPUContext->eglDisplay,
_gles3GPUContext->eglConfig,
_gles3GPUContext->eglDefaultContext);
#endif
} else if (gfxApi == gfx::API::VULKAN) {
#if CC_USE_VULKAN
int vkQueueFamilyIndex = xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::VK_QUEUE_FAMILY_INDEX).getInt();
cc::xr::XrEntry::getInstance()->initXrSession(_vkInstance, _vkPhysicalDevice, _vkDevice, vkQueueFamilyIndex);
#endif
}
#else
CC_UNUSED_PARAM(gfxApi);
#endif
}
const xr::XRSwapchain &XRInterface::doGFXDeviceAcquire(gfx::API gfxApi) {
#if CC_USE_XR
// CC_LOG_INFO("[XR] doGFXDeviceAcquire.api.%d", gfxApi);
if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
return xr::XrEntry::getInstance()->acquireXrSwapchain(static_cast<uint32_t>(gfxApi));
}
#else
CC_UNUSED_PARAM(gfxApi);
#endif
return _acquireSwapchain;
}
bool XRInterface::isGFXDeviceNeedsPresent(gfx::API gfxApi) {
CC_UNUSED_PARAM(gfxApi);
#if CC_USE_XR
// CC_LOG_INFO("[XR] isGFXDeviceNeedsPresent.api.%d", gfxApi);
// if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
// }
return xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::PRESENT_ENABLE).getBool();
#else
return true;
#endif
}
void XRInterface::postGFXDevicePresent(gfx::API gfxApi) {
#if CC_USE_XR
// CC_LOG_INFO("[XR] postGFXDevicePresent.api.%d", gfxApi);
if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
}
#else
CC_UNUSED_PARAM(gfxApi);
#endif
}
void XRInterface::createXRSwapchains() {
#if CC_USE_XR
CC_LOG_INFO("[XR] createXRSwapchains");
if (gfx::DeviceAgent::getInstance()) {
ENQUEUE_MESSAGE_0(gfx::DeviceAgent::getInstance()->getMessageQueue(),
CreateXRSwapchains,
{
CC_LOG_INFO("[XR] [RT] initXrSwapchains");
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::RENDER_THREAD_ID, (int)gettid());
JniHelper::getEnv();
xr::XrEntry::getInstance()->initXrSwapchains();
})
} else {
xr::XrEntry::getInstance()->initXrSwapchains();
}
#endif
}
const std::vector<cc::xr::XRSwapchain> &XRInterface::getXRSwapchains() {
#if CC_USE_XR
CC_LOG_INFO("[XR] getXRSwapchains");
if (_xrSwapchains.empty()) {
_xrSwapchains = xr::XrEntry::getInstance()->getCocosXrSwapchains();
}
#endif
return _xrSwapchains;
}
gfx::Format XRInterface::getXRSwapchainFormat() {
#if CC_USE_XR
int swapchainFormat = xr::XrEntry::getInstance()->getXRConfig(xr::XRConfigKey::SWAPCHAIN_FORMAT).getInt();
#if CC_USE_GLES3
if (swapchainFormat == GL_SRGB_ALPHA_EXT) {
return gfx::Format::SRGB8_A8;
}
if (swapchainFormat == GL_RGBA8) {
return gfx::Format::RGBA8;
}
if (swapchainFormat == GL_BGRA8_EXT) {
return gfx::Format::BGRA8;
}
#endif
#if CC_USE_VULKAN
if (swapchainFormat == VK_FORMAT_R8G8B8A8_SRGB) {
return gfx::Format::SRGB8_A8;
}
if (swapchainFormat == VK_FORMAT_R8G8B8A8_UNORM) {
return gfx::Format::RGBA8;
}
if (swapchainFormat == VK_FORMAT_B8G8R8A8_UNORM) {
return gfx::Format::BGRA8;
}
#endif
#endif
return gfx::Format::BGRA8;
}
void XRInterface::updateXRSwapchainTypedID(uint32_t typedID) {
#if CC_USE_XR
#if XR_OEM_HUAWEIVR
_eglSurfaceTypeMap[typedID] = EGLSurfaceType::WINDOW;
#else
_eglSurfaceTypeMap[typedID] = EGLSurfaceType::PBUFFER;
#endif
#else
CC_UNUSED_PARAM(typedID);
#endif
}
// gfx
// vulkan
#ifdef CC_USE_VULKAN
uint32_t XRInterface::getXRVkApiVersion(uint32_t engineVkApiVersion) {
#if CC_USE_XR
return xr::XrEntry::getInstance()->getXrVkApiVersion(engineVkApiVersion);
#else
return engineVkApiVersion;
#endif
}
void XRInterface::initializeVulkanData(const PFN_vkGetInstanceProcAddr &addr) {
_vkGetInstanceProcAddr = addr;
}
VkInstance XRInterface::createXRVulkanInstance(const VkInstanceCreateInfo &instInfo) {
#if CC_USE_XR
_vkInstance = xr::XrEntry::getInstance()->xrVkCreateInstance(instInfo, _vkGetInstanceProcAddr);
_vkPhysicalDevice = xr::XrEntry::getInstance()->getXrVkGraphicsDevice(_vkInstance);
return _vkInstance;
#else
CC_UNUSED_PARAM(instInfo);
return nullptr;
#endif
}
VkDevice XRInterface::createXRVulkanDevice(const VkDeviceCreateInfo *deviceInfo) {
#if CC_USE_XR
VK_CHECK(xr::XrEntry::getInstance()->xrVkCreateDevice(deviceInfo, _vkGetInstanceProcAddr, _vkPhysicalDevice, &_vkDevice));
#else
CC_UNUSED_PARAM(deviceInfo);
#endif
return _vkDevice;
}
VkPhysicalDevice XRInterface::getXRVulkanGraphicsDevice() {
return _vkPhysicalDevice;
}
void XRInterface::getXRSwapchainVkImages(std::vector<VkImage> &vkImages, uint32_t eye) {
#if CC_USE_XR
xr::XrEntry::getInstance()->getSwapchainImages(vkImages, eye);
#else
CC_UNUSED_PARAM(vkImages);
CC_UNUSED_PARAM(eye);
#endif
}
#endif
// vulkan
// gles
#ifdef CC_USE_GLES3
void XRInterface::initializeGLESData(PFNGLES3WLOADPROC gles3wLoadFuncProc, gfx::GLES3GPUContext *gpuContext) {
#if CC_USE_XR
_gles3wLoadFuncProc = gles3wLoadFuncProc;
_gles3GPUContext = gpuContext;
void *eglDisplay = gpuContext->eglDisplay;
void *eglConfig = gpuContext->eglConfig;
void *eglDefaultContext = gpuContext->eglDefaultContext;
CC_LOG_INFO("[XR] initializeGLESData.egl.%p/%p/%p", eglDisplay, eglConfig, eglDefaultContext);
#else
CC_UNUSED_PARAM(gles3wLoadFuncProc);
CC_UNUSED_PARAM(gpuContext);
#endif
}
void XRInterface::attachGLESFramebufferTexture2D() {
#if CC_USE_XR
xr::XrEntry::getInstance()->attachXrFramebufferTexture2D();
#endif
}
EGLSurfaceType XRInterface::acquireEGLSurfaceType(uint32_t typedID) {
#if CC_USE_XR
if (_eglSurfaceTypeMap.count(typedID) > 0) {
return _eglSurfaceTypeMap[typedID];
}
#else
CC_UNUSED_PARAM(typedID);
#endif
return EGLSurfaceType::WINDOW;
}
#endif
// gles
// stereo render loop
bool XRInterface::platformLoopStart() {
#if CC_USE_XR
// CC_LOG_INFO("[XR] platformLoopStart");
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager && !_xrRemotePreviewManager->isStarted()) {
_xrRemotePreviewManager->start();
}
#endif
return xr::XrEntry::getInstance()->platformLoopStart();
#else
return false;
#endif
}
bool XRInterface::beginRenderFrame() {
#if CC_USE_XR
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] beginRenderFrame.%d", _committedFrame);
if (gfx::DeviceAgent::getInstance()) {
static uint64_t frameId = 0;
frameId++;
if (_committedFrame) {
gfx::DeviceAgent::getInstance()->presentWait();
_committedFrame = false;
}
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] beginRenderFrame waitFrame.%lld", frameId);
xr::XrEntry::getInstance()->waitFrame();
ENQUEUE_MESSAGE_1(gfx::DeviceAgent::getInstance()->getMessageQueue(),
BeginRenderFrame, frameId, frameId,
{
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] beginRenderFrame.%lld", frameId);
xr::XrEntry::getInstance()->frameStart();
})
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
_xrRemotePreviewManager->tick();
}
#endif
return true;
}
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
_xrRemotePreviewManager->tick();
}
#endif
return xr::XrEntry::getInstance()->frameStart();
#else
return false;
#endif
}
bool XRInterface::isRenderAllowable() {
#if CC_USE_XR
return xr::XrEntry::getInstance()->isRenderAllowable();
#else
return false;
#endif
}
bool XRInterface::beginRenderEyeFrame(uint32_t eye) {
#if CC_USE_XR
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] beginRenderEyeFrame %d", eye);
if (gfx::DeviceAgent::getInstance()) {
ENQUEUE_MESSAGE_1(gfx::DeviceAgent::getInstance()->getMessageQueue(),
BeginRenderEyeFrame, eye, eye,
{
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] beginRenderEyeFrame %d", eye);
xr::XrEntry::getInstance()->renderLoopStart(eye);
})
} else {
xr::XrEntry::getInstance()->renderLoopStart(static_cast<int>(eye));
}
#else
CC_UNUSED_PARAM(eye);
#endif
return true;
}
bool XRInterface::endRenderEyeFrame(uint32_t eye) {
#if CC_USE_XR
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] endRenderEyeFrame %d", eye);
if (gfx::DeviceAgent::getInstance()) {
ENQUEUE_MESSAGE_1(gfx::DeviceAgent::getInstance()->getMessageQueue(),
EndRenderEyeFrame, eye, eye,
{
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] endRenderEyeFrame %d", eye);
xr::XrEntry::getInstance()->renderLoopEnd(eye);
})
} else {
xr::XrEntry::getInstance()->renderLoopEnd(static_cast<int>(eye));
}
#else
CC_UNUSED_PARAM(eye);
#endif
return true;
}
bool XRInterface::endRenderFrame() {
#if CC_USE_XR
if (IS_ENABLE_XR_LOG) {
CC_LOG_INFO("[XR] endRenderFrame.%d",
cc::ApplicationManager::getInstance()->getCurrentAppSafe()->getEngine()->getTotalFrames());
}
if (gfx::DeviceAgent::getInstance()) {
ENQUEUE_MESSAGE_0(gfx::DeviceAgent::getInstance()->getMessageQueue(),
EndRenderFrame,
{
xr::XrEntry::getInstance()->frameEnd();
gfx::DeviceAgent::getInstance()->presentSignal();
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] presentSignal endRenderFrame.%d",
cc::ApplicationManager::getInstance()->getCurrentAppSafe()->getEngine()->getTotalFrames());
})
_committedFrame = true;
// CC_LOG_INFO("[XR] endRenderFrame pass presentWait errno %d", errno);
} else {
xr::XrEntry::getInstance()->frameEnd();
#if CC_USE_XR_REMOTE_PREVIEW
if (_xrRemotePreviewManager) {
_xrRemotePreviewManager->tick();
}
#endif
}
#endif
return true;
}
bool XRInterface::platformLoopEnd() {
#if CC_USE_XR
return xr::XrEntry::getInstance()->platformLoopEnd();
#else
return false;
#endif
}
// stereo render loop
ccstd::vector<float> XRInterface::getHMDViewPosition(uint32_t eye, int trackingType) {
#if CC_USE_XR
return xr::XrEntry::getInstance()->getHMDViewPosition(eye, trackingType);
#else
CC_UNUSED_PARAM(eye);
CC_UNUSED_PARAM(trackingType);
ccstd::vector<float> res;
res.reserve(3);
return res;
#endif
}
ccstd::vector<float> XRInterface::getXREyeFov(uint32_t eye) {
#if CC_USE_XR
return xr::XrEntry::getInstance()->getEyeFov(eye);
#else
CC_UNUSED_PARAM(eye);
ccstd::vector<float> res;
res.reserve(4);
return res;
#endif
}
ccstd::vector<float> XRInterface::getXRViewProjectionData(uint32_t eye, float near, float far) {
#if CC_USE_XR
return xr::XrEntry::getInstance()->computeViewProjection(eye, near, far, 1.0F);
#else
CC_UNUSED_PARAM(eye);
CC_UNUSED_PARAM(near);
CC_UNUSED_PARAM(far);
ccstd::vector<float> res;
res.reserve(16);
return res;
#endif
}
// renderwindow
xr::XREye XRInterface::getXREyeByRenderWindow(void *window) {
if (!window) {
return xr::XREye::NONE;
}
if (_xrWindowMap.count(window) > 0) {
return _xrWindowMap[window];
}
return xr::XREye::NONE;
}
void XRInterface::bindXREyeWithRenderWindow(void *window, xr::XREye eye) {
if (_xrWindowMap.count(window) > 0) {
_xrWindowMap[window] = eye;
} else {
_xrWindowMap.emplace(std::make_pair(window, eye));
}
}
void XRInterface::loadAssetsImage(const std::string &imageInfo) {
// name|@assets/TrackingImage_SpacesTown.png|0.18|0.26
ccstd::vector<ccstd::string> segments = StringUtil::split(imageInfo, "|");
std::string imageName = segments.at(0);
std::string imagePath = segments.at(1);
auto physicalSizeX = static_cast<float>(atof(segments.at(2).c_str()));
auto physicalSizeY = static_cast<float>(atof(segments.at(3).c_str()));
auto *spaceTownImage = new Image();
spaceTownImage->addRef();
bool res = spaceTownImage->initWithImageFile(imagePath);
if (!res) {
CC_LOG_ERROR("[XRInterface] loadAssetsImage init failed, %s!!!", imageInfo.c_str());
return;
}
uint32_t imageWidth = spaceTownImage->getWidth();
uint32_t imageHeight = spaceTownImage->getHeight();
const uint32_t bufferSize = imageWidth * imageHeight * 3;
auto *buffer = new uint8_t[bufferSize];
for (unsigned int j = 0; j < imageHeight; ++j) {
for (unsigned int i = 0; i < imageWidth; ++i) {
const unsigned int pixel = i + j * imageWidth;
const unsigned int pixelFlip = i + (imageHeight - j - 1) * imageWidth;
const uint8_t *originalPixel = &spaceTownImage->getData()[static_cast<size_t>(pixel * 4)];
uint8_t *convertedPixel = &buffer[static_cast<size_t>(pixelFlip * 3)];
convertedPixel[0] = originalPixel[0];
convertedPixel[1] = originalPixel[1];
convertedPixel[2] = originalPixel[2];
}
}
spaceTownImage->release();
auto app = CC_CURRENT_APPLICATION();
if (!app) {
CC_LOG_ERROR("[XRInterface] loadAssetsImage callback failed, application not exist!!!");
return;
}
auto engine = app->getEngine();
CC_ASSERT(engine != nullptr);
engine->getScheduler()->performFunctionInCocosThread([=]() {
xr::XRTrackingImageData candidateImage;
candidateImage.friendlyName = imageName;
candidateImage.bufferSize = bufferSize;
candidateImage.buffer = buffer;
candidateImage.pixelSizeWidth = imageWidth;
candidateImage.pixelSizeHeight = imageHeight;
candidateImage.physicalWidth = physicalSizeX;
candidateImage.physicalHeight = physicalSizeY;
setXRConfig(xr::XRConfigKey::IMAGE_TRACKING_CANDIDATEIMAGE, static_cast<void *>(&candidateImage));
});
}
} // namespace cc