diff --git a/CMakeLists.txt b/CMakeLists.txt index 31b9bee36f5b..bc62b35ff5b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,9 @@ include_directories(ext/glslang) # Anyway, glew will be going away anyway. include_directories(ext/glew) -if(NOT OPENGL_LIBRARIES AND USING_GLES2) +if(OPENXR) + set(OPENGL_LIBRARIES GLESv3 EGL) +elseif(NOT OPENGL_LIBRARIES AND USING_GLES2) set(OPENGL_LIBRARIES GLESv2 EGL) endif() @@ -1079,6 +1081,8 @@ if(ANDROID) if (OPENXR) set(nativeExtra ${nativeExtra} + Common/VR/PPSSPPVR.cpp + Common/VR/PPSSPPVR.h Common/VR/VRBase.cpp Common/VR/VRBase.h Common/VR/VRFramebuffer.cpp diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 3d3c30e4337c..d7212985faa7 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -6,6 +6,7 @@ #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/GPU/OpenGL/DataFormatGL.h" #include "Common/Math/math_util.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/Log.h" #include "Common/MemoryUtil.h" @@ -30,11 +31,6 @@ static constexpr int TEXCACHE_NAME_CACHE_SIZE = 16; extern void bindDefaultFBO(); #endif -#ifdef OPENXR -#include "VR/VRBase.h" -#include "VR/VRRenderer.h" -#endif - // Workaround for Retroarch. Simply declare // extern GLuint g_defaultFBO; // and set is as appropriate. Can adjust the variables in ext/native/base/display.h as @@ -110,6 +106,19 @@ static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) { return infoLog; } +int GLQueueRunner::GetStereoBufferIndex(const char *uniformName) { + if (!uniformName) return -1; + else if (strcmp(uniformName, "u_view") == 0) return 0; + else if (strcmp(uniformName, "u_proj_lens") == 0) return 1; + else return -1; +} + +std::string GLQueueRunner::GetStereoBufferLayout(const char *uniformName) { + if (strcmp(uniformName, "u_view") == 0) return "ViewMatrices"; + else if (strcmp(uniformName, "u_proj_lens") == 0) return "ProjectionMatrix"; + else return "undefined"; +} + void GLQueueRunner::RunInitSteps(const std::vector &steps, bool skipGLCalls) { if (skipGLCalls) { // Some bookkeeping still needs to be done. @@ -263,7 +272,27 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps, bool ski for (size_t j = 0; j < program->queries_.size(); j++) { auto &query = program->queries_[j]; _dbg_assert_(query.name); - int location = glGetUniformLocation(program->program, query.name); + + int location = -1; + if (IsVRBuild() && IsMultiviewSupported()) { + int index = GetStereoBufferIndex(query.name); + if (index >= 0) { + std::string layout = GetStereoBufferLayout(query.name); + glUniformBlockBinding(program->program, glGetUniformBlockIndex(program->program, layout.c_str()), index); + + GLuint buffer = 0; + glGenBuffers(1, &buffer); + glBindBuffer(GL_UNIFORM_BUFFER, buffer); + glBufferData(GL_UNIFORM_BUFFER,2 * 16 * sizeof(float),NULL, GL_STATIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + location = buffer; + } else { + location = glGetUniformLocation(program->program, query.name); + } + } else { + location = glGetUniformLocation(program->program, query.name); + } + if (location < 0 && query.required) { WARN_LOG(G3D, "Required uniform query for '%s' failed", query.name); } @@ -613,7 +642,7 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { currentReadHandle_ = fbo->handle; } -void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCalls) { +void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps) { if (skipGLCalls) { // Dry run for (size_t i = 0; i < steps.size(); i++) { @@ -695,7 +724,9 @@ void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCal glPopDebugGroup(); #endif - delete steps[i]; + if (!keepSteps) { + delete steps[i]; + } } CHECK_GL_ERROR_IF_DEBUG(); } @@ -995,6 +1026,36 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last CHECK_GL_ERROR_IF_DEBUG(); break; } + case GLRRenderCommand::UNIFORMSTEREOMATRIX: + { + _dbg_assert_(curProgram); + if (IsMultiviewSupported()) { + int layout = GetStereoBufferIndex(c.uniformMatrix4.name); + if (layout >= 0) { + int size = 2 * 16 * sizeof(float); + glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformMatrix4.loc); + glBindBuffer(GL_UNIFORM_BUFFER, *c.uniformMatrix4.loc); + void *matrices = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + memcpy(matrices, c.uniformMatrix4.m, size); + glUnmapBuffer(GL_UNIFORM_BUFFER); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + } else { + int loc = c.uniformMatrix4.loc ? *c.uniformMatrix4.loc : -1; + if (c.uniformMatrix4.name) { + loc = curProgram->GetUniformLoc(c.uniformMatrix4.name); + } + if (loc >= 0) { + if (GetVRFBOIndex() == 0) { + glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m); + } else { + glUniformMatrix4fv(loc, 1, false, &c.uniformMatrix4.m[16]); + } + } + } + CHECK_GL_ERROR_IF_DEBUG(); + break; + } case GLRRenderCommand::UNIFORMMATRIX: { _dbg_assert_(curProgram); @@ -1665,9 +1726,9 @@ void GLQueueRunner::fbo_unbind() { bindDefaultFBO(); #endif -#ifdef OPENXR - VR_BindFramebuffer(VR_GetEngine(), 0); -#endif + if (IsVRBuild()) { + BindVRFramebuffer(); + } currentDrawHandle_ = 0; currentReadHandle_ = 0; diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index e82b265ed183..1e009787aef3 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -47,6 +47,7 @@ enum class GLRRenderCommand : uint8_t { UNIFORM4UI, UNIFORM4F, UNIFORMMATRIX, + UNIFORMSTEREOMATRIX, TEXTURESAMPLER, TEXTURELOD, VIEWPORT, @@ -128,7 +129,7 @@ struct GLRRenderData { struct { const char *name; // if null, use loc const GLint *loc; - float m[16]; + float m[32]; } uniformMatrix4; struct { uint32_t clearColor; @@ -357,9 +358,12 @@ class GLQueueRunner { caps_ = caps; } + int GetStereoBufferIndex(const char *uniformName); + std::string GetStereoBufferLayout(const char *uniformName); + void RunInitSteps(const std::vector &steps, bool skipGLCalls); - void RunSteps(const std::vector &steps, bool skipGLCalls); + void RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps = false); void LogSteps(const std::vector &steps); void CreateDeviceObjects(); diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 3fcca9eaf8d6..efdf0bd7f479 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -3,17 +3,14 @@ #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/GPU/thin3d.h" #include "Common/Thread/ThreadUtil.h" +#include "Common/VR/PPSSPPVR.h" + +#include "Core/Config.h" #include "Common/Log.h" #include "Common/MemoryUtil.h" #include "Common/Math/math_util.h" -#ifdef OPENXR -#include "Core/Config.h" -#include "VR/VRBase.h" -#include "VR/VRRenderer.h" -#endif - #if 0 // def _DEBUG #define VLOG(...) INFO_LOG(G3D, __VA_ARGS__) #else @@ -203,22 +200,6 @@ bool GLRenderManager::ThreadFrame() { std::unique_lock lock(mutex_); if (!run_) return false; -#ifdef OPENXR - VR_BeginFrame(VR_GetEngine()); - - // Decide if the scene is 3D or not - if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) { - VR_SetConfig(VR_CONFIG_MODE, VR_MODE_MONO_6DOF); - } else { - VR_SetConfig(VR_CONFIG_MODE, VR_MODE_FLAT_SCREEN); - } - VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) / 2); - - // Set customizations - VR_SetConfig(VR_CONFIG_6DOF_ENABLED, g_Config.bEnable6DoF); - VR_SetConfig(VR_CONFIG_CANVAS_DISTANCE, g_Config.iCanvasDistance); - VR_SetConfig(VR_CONFIG_FOV_SCALE, g_Config.iFieldOfViewPercentage); -#endif // In case of syncs or other partial completion, we keep going until we complete a frame. do { @@ -255,11 +236,10 @@ bool GLRenderManager::ThreadFrame() { firstFrame = false; } Run(threadFrame_); + VLOG("PULL: Finished frame %d", threadFrame_); } while (!nextFrame); -#ifdef OPENXR - VR_EndFrame(VR_GetEngine()); -#endif + return true; } @@ -581,6 +561,7 @@ void GLRenderManager::EndSubmitFrame(int frame) { void GLRenderManager::Run(int frame) { BeginSubmitFrame(frame); + FrameData &frameData = frameData_[frame]; auto &stepsOnThread = frameData_[frame].steps; @@ -597,7 +578,22 @@ void GLRenderManager::Run(int frame) { } } - queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); + if (IsVRBuild()) { + if (PreVRRender()) { + int passes = 1; + if (!IsMultiviewSupported() && g_Config.bEnableStereo) { + passes = 2; + } + for (int i = 0; i < passes; i++) { + PreVRFrameRender(i); + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1); + PostVRFrameRender(); + } + PostVRRender(); + } + } else { + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); + } stepsOnThread.clear(); if (!skipGLCalls_) { diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 6be44a70eed9..5423262c9827 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -743,6 +743,19 @@ class GLRenderManager { curRenderStep_->commands.push_back(data); } + void SetUniformM4x4Stereo(const char *name, const GLint *loc, const float *left, const float *right) { + _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + _dbg_assert_(curProgram_); +#endif + GLRRenderData data{ GLRRenderCommand::UNIFORMSTEREOMATRIX }; + data.uniformMatrix4.name = name; + data.uniformMatrix4.loc = loc; + memcpy(&data.uniformMatrix4.m[0], left, sizeof(float) * 16); + memcpy(&data.uniformMatrix4.m[16], right, sizeof(float) * 16); + curRenderStep_->commands.push_back(data); + } + void SetUniformM4x4(const char *name, const float *udata) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); #ifdef _DEBUG diff --git a/Common/GPU/Vulkan/VulkanLoader.cpp b/Common/GPU/Vulkan/VulkanLoader.cpp index b493dc473eb9..b0c2ab370b9e 100644 --- a/Common/GPU/Vulkan/VulkanLoader.cpp +++ b/Common/GPU/Vulkan/VulkanLoader.cpp @@ -23,6 +23,7 @@ #include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/Log.h" #include "Common/System/System.h" +#include "Common/VR/PPSSPPVR.h" #if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(SWITCH) #include @@ -308,10 +309,10 @@ void VulkanSetAvailable(bool available) { bool VulkanMayBeAvailable() { -#ifdef OPENXR - //unsupported at the moment - return false; -#endif + //unsupported in VR at the moment + if (IsVRBuild()) { + return false; + } if (g_vulkanAvailabilityChecked) { return g_vulkanMayBeAvailable; diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index c3285450c92b..8026de37bcba 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -12,15 +12,11 @@ #include "Common/UI/Root.h" #include "Common/Data/Text/I18n.h" #include "Common/Render/DrawBuffer.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/Log.h" #include "Common/StringUtils.h" -#ifdef OPENXR -#include "VR/VRBase.h" -#include "VR/VRRenderer.h" -#endif - static const bool ClickDebug = false; UIScreen::UIScreen() @@ -93,9 +89,9 @@ void UIScreen::preRender() { draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI"); screenManager()->getUIContext()->BeginFrame(); -#ifdef OPENXR - VR_BindFramebuffer(VR_GetEngine(), 0); -#endif + if (IsVRBuild()) { + BindVRFramebuffer(); + } Draw::Viewport viewport; viewport.TopLeftX = 0; diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp new file mode 100644 index 000000000000..16b170e1fb57 --- /dev/null +++ b/Common/VR/PPSSPPVR.cpp @@ -0,0 +1,293 @@ +#include "Common/VR/PPSSPPVR.h" +#include "Common/VR/VRBase.h" +#include "Common/VR/VRInput.h" +#include "Common/VR/VRMath.h" +#include "Common/VR/VRRenderer.h" +#include "Common/VR/VRTweaks.h" + +#include "Core/HLE/sceDisplay.h" +#include "Core/Config.h" +#include "Core/KeyMap.h" + + +/* +================================================================================ + +VR button mapping + +================================================================================ +*/ + +struct ButtonMapping { + ovrButton ovr; + int keycode; + bool pressed; + int repeat; + + ButtonMapping(int keycode, ovrButton ovr) { + this->keycode = keycode; + this->ovr = ovr; + pressed = false; + repeat = 0; + } +}; + +struct MouseActivator { + bool activate; + ovrButton ovr; + + MouseActivator(bool activate, ovrButton ovr) { + this->activate = activate; + this->ovr = ovr; + } +}; + +static std::vector leftControllerMapping = { + ButtonMapping(NKCODE_BUTTON_X, ovrButton_X), + ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y), + ButtonMapping(NKCODE_ALT_LEFT, ovrButton_GripTrigger), + ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up), + ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down), + ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left), + ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right), + ButtonMapping(NKCODE_BUTTON_THUMBL, ovrButton_LThumb), + ButtonMapping(NKCODE_ENTER, ovrButton_Trigger), + ButtonMapping(NKCODE_BACK, ovrButton_Enter), +}; + +static std::vector rightControllerMapping = { + ButtonMapping(NKCODE_BUTTON_A, ovrButton_A), + ButtonMapping(NKCODE_BUTTON_B, ovrButton_B), + ButtonMapping(NKCODE_ALT_RIGHT, ovrButton_GripTrigger), + ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up), + ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down), + ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left), + ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right), + ButtonMapping(NKCODE_BUTTON_THUMBR, ovrButton_RThumb), + ButtonMapping(NKCODE_ENTER, ovrButton_Trigger), +}; + +static const int controllerIds[] = {DEVICE_ID_XR_CONTROLLER_LEFT, DEVICE_ID_XR_CONTROLLER_RIGHT}; +static std::vector controllerMapping[2] = { + leftControllerMapping, + rightControllerMapping +}; +static int mouseController = -1; +static bool mousePressed[] = {false, false}; + +static std::vector mouseActivators = { + MouseActivator(true, ovrButton_Trigger), + MouseActivator(false, ovrButton_Up), + MouseActivator(false, ovrButton_Down), + MouseActivator(false, ovrButton_Left), + MouseActivator(false, ovrButton_Right), +}; + +/* +================================================================================ + +VR app flow integration + +================================================================================ +*/ + +bool IsVRBuild() { + return true; +} + +void InitVROnAndroid(void* vm, void* activity, int version, char* name) { + ovrJava java; + java.Vm = (JavaVM*)vm; + java.ActivityObject = (jobject)activity; + java.AppVersion = version; + strcpy(java.AppName, name); + VR_Init(java); + + __DisplaySetFramerate(72); +} + +void EnterVR(bool firstStart) { + if (firstStart) { + VR_EnterVR(VR_GetEngine()); + IN_VRInit(VR_GetEngine()); + } + VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, false); +} + +void GetVRResolutionPerEye(int* width, int* height) { + if (VR_GetEngine()->appState.Instance) { + VR_GetResolution(VR_GetEngine(), width, height); + } +} + +void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) { + //buttons + KeyInput keyInput = {}; + for (int j = 0; j < 2; j++) { + int status = IN_VRGetButtonState(j); + for (ButtonMapping& m : controllerMapping[j]) { + bool pressed = status & m.ovr; + keyInput.flags = pressed ? KEY_DOWN : KEY_UP; + keyInput.keyCode = m.keycode; + keyInput.deviceId = controllerIds[j]; + + if (m.pressed != pressed) { + if (pressed && haptics) { + INVR_Vibrate(100, j, 1000); + } + NativeKey(keyInput); + m.pressed = pressed; + m.repeat = 0; + } else if (pressed && (m.repeat > 30)) { + keyInput.flags |= KEY_IS_REPEAT; + NativeKey(keyInput); + m.repeat = 0; + } else { + m.repeat++; + } + } + } + + //enable or disable mouse + for (int j = 0; j < 2; j++) { + int status = IN_VRGetButtonState(j); + for (MouseActivator& m : mouseActivators) { + if (status & m.ovr) { + mouseController = m.activate ? j : -1; + } + } + } + + //mouse cursor + if (mouseController >= 0) { + //get position on screen + XrPosef pose = IN_VRGetPose(mouseController); + XrVector3f angles = XrQuaternionf_ToEulerAngles(pose.orientation); + float width = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_WIDTH); + float height = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_HEIGHT); + float cx = width / 2; + float cy = height / 2; + float speed = (cx + cy) / 2; + float x = cx - tan(ToRadians(angles.y - (float)VR_GetConfig(VR_CONFIG_MENU_YAW))) * speed; + float y = cy - tan(ToRadians(angles.x)) * speed; + + //set renderer + VR_SetConfig(VR_CONFIG_MOUSE_X, (int)x); + VR_SetConfig(VR_CONFIG_MOUSE_Y, (int)y); + VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 6 * (int)pow(VR_GetConfig(VR_CONFIG_CANVAS_DISTANCE), 0.25f)); + + //inform engine about the status + TouchInput touch; + touch.id = mouseController; + touch.x = x * dp_xscale; + touch.y = (height - y - 1) * dp_yscale; + bool pressed = IN_VRGetButtonState(mouseController) & ovrButton_Trigger; + if (mousePressed[mouseController] != pressed) { + if (!pressed) { + touch.flags = TOUCH_DOWN; + NativeTouch(touch); + touch.flags = TOUCH_UP; + NativeTouch(touch); + } + mousePressed[mouseController] = pressed; + } + } else { + VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 0); + } +} + +void UpdateVRScreenKey(const KeyInput &key) { + std::vector nativeKeys; + if (KeyMap::KeyToPspButton(key.deviceId, key.keyCode, &nativeKeys)) { + for (int& nativeKey : nativeKeys) { + if (nativeKey == CTRL_SCREEN) { + VR_SetConfig(VR_CONFIG_FORCE_2D, key.flags & KEY_DOWN); + } + } + } +} + +/* +================================================================================ + +VR rendering integration + +================================================================================ +*/ + +void BindVRFramebuffer() { + VR_BindFramebuffer(VR_GetEngine()); +} + +bool PreVRRender() { + if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) { + VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported()); + VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true); + } + + if (VR_InitFrame(VR_GetEngine())) { + + // Decide if the scene is 3D or not + if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) { + VR_SetConfig(VR_CONFIG_MODE, g_Config.bEnableStereo ? VR_MODE_STEREO_6DOF : VR_MODE_MONO_6DOF); + } else { + VR_SetConfig(VR_CONFIG_MODE, VR_MODE_FLAT_SCREEN); + } + VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) / 2); + + // Set customizations + VR_SetConfig(VR_CONFIG_6DOF_ENABLED, g_Config.bEnable6DoF); + VR_SetConfig(VR_CONFIG_CANVAS_DISTANCE, g_Config.iCanvasDistance); + VR_SetConfig(VR_CONFIG_FOV_SCALE, g_Config.iFieldOfViewPercentage); + VR_SetConfig(VR_CONFIG_STEREO_SEPARATION, g_Config.iStereoSeparation); + return true; + } + return false; +} + +void PostVRRender() { + VR_FinishFrame(VR_GetEngine()); +} + +void PreVRFrameRender(int fboIndex) { + VR_BeginFrame(VR_GetEngine(), fboIndex); +} + +void PostVRFrameRender() { + VR_EndFrame(VR_GetEngine()); +} + +int GetVRFBOIndex() { + return VR_GetConfig(VR_CONFIG_CURRENT_FBO); +} + +bool IsMultiviewSupported() { + return false; +} + +bool IsFlatVRScene() { + return VR_GetConfig(VR_CONFIG_MODE) == VR_MODE_FLAT_SCREEN; +} + +bool Is2DVRObject(float* projMatrix, bool ortho) { + bool is2D = VR_TweakIsMatrixBigScale(projMatrix) || + VR_TweakIsMatrixIdentity(projMatrix) || + VR_TweakIsMatrixOneOrtho(projMatrix) || + VR_TweakIsMatrixOneScale(projMatrix) || + VR_TweakIsMatrixOneTransform(projMatrix); + if (!is2D && !ortho) { + VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) + 1); + } + return is2D; +} + +void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye) { + VR_TweakProjection(projMatrix, leftEye, VR_PROJECTION_MATRIX_LEFT_EYE); + VR_TweakProjection(projMatrix, rightEye, VR_PROJECTION_MATRIX_RIGHT_EYE); + VR_TweakMirroring(projMatrix); +} + +void UpdateVRView(float* projMatrix, float* leftEye, float* rightEye) { + VR_TweakView(leftEye, projMatrix, VR_VIEW_MATRIX_LEFT_EYE); + VR_TweakView(rightEye, projMatrix, VR_VIEW_MATRIX_RIGHT_EYE); +} diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h new file mode 100644 index 000000000000..b41b69f1be9c --- /dev/null +++ b/Common/VR/PPSSPPVR.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Common/Input/InputState.h" +#include "Common/Input/KeyCodes.h" + +#ifdef OPENXR + +// VR app flow integration +bool IsVRBuild(); +void InitVROnAndroid(void* vm, void* activity, int version, char* name); +void EnterVR(bool firstStart); +void GetVRResolutionPerEye(int* width, int* height); +void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale); +void UpdateVRScreenKey(const KeyInput &key); + +// VR rendering integration +void BindVRFramebuffer(); +bool PreVRRender(); +void PostVRRender(); +void PreVRFrameRender(int fboIndex); +void PostVRFrameRender(); +int GetVRFBOIndex(); +bool IsMultiviewSupported(); +bool IsFlatVRScene(); +bool Is2DVRObject(float* projMatrix, bool ortho); +void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye); +void UpdateVRView(float* projMatrix, float* leftEye, float* rightEye); + +#else //dummy integration + +// VR app flow integration +inline bool IsVRBuild() { return false; } +inline void InitVROnAndroid(void* vm, void* activity, int version, char* name) {} +inline void EnterVR(bool firstTime) {} +inline void GetVRResolutionPerEye(int* width, int* height) {} +inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {} +inline void UpdateVRScreenKey(const KeyInput &key) {} + +// VR rendering integration +inline void BindVRFramebuffer() {} +inline bool PreVRRender() { return false; } +inline void PostVRRender() {} +inline void PreVRFrameRender(int fboIndex) {} +inline void PostVRFrameRender() {} +inline int GetVRFBOIndex() { return 0; } +inline bool IsMultiviewSupported() { return false; } +inline bool IsFlatVRScene() { return true; } +inline bool Is2DVRObject(float* projMatrix, bool ortho) { return false; } +inline void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye) {} +inline void UpdateVRView(float* projMatrix, float* leftEye, float* rightEye) {} + +#endif diff --git a/Common/VR/VRBase.cpp b/Common/VR/VRBase.cpp index 56745116e739..9932d095535d 100644 --- a/Common/VR/VRBase.cpp +++ b/Common/VR/VRBase.cpp @@ -10,6 +10,10 @@ int vr_initialized = 0; const char* const requiredExtensionNames[] = { XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, +#ifdef OPENXR_HAS_PERFORMANCE_EXTENSION + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, +#endif XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME}; const uint32_t numRequiredExtensions = sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); diff --git a/Common/VR/VRBase.h b/Common/VR/VRBase.h index 6aac2486c6f2..fffd234868a3 100644 --- a/Common/VR/VRBase.h +++ b/Common/VR/VRBase.h @@ -1,6 +1,150 @@ #pragma once -#include "VRFramebuffer.h" +#ifdef ANDROID +#include +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "OpenXR", __VA_ARGS__); +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "OpenXR", __VA_ARGS__); +#else +#define ALOGE(...) printf(__VA_ARGS__) +#define ALOGV(...) printf(__VA_ARGS__) +#endif + +//OpenXR +#define XR_USE_PLATFORM_ANDROID 1 +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _DEBUG +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +static void GLCheckErrors(char* file, int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %s:%d %s", file, line, GlErrorString(error)); + } +} + +#define GL(func) func; GLCheckErrors(__FILE__ , __LINE__); +#else +#define GL(func) func; +#endif + +#if defined(_DEBUG) +static void OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#define OXR(func) OXR_CheckErrors(VR_GetEngine()->appState.Instance, func, #func, true); +#else +#define OXR(func) func; +#endif + +#define OPENXR_HAS_PERFORMANCE_EXTENSION + +enum { ovrMaxLayerCount = 1 }; +enum { ovrMaxNumEyes = 2 }; + +typedef union { + XrCompositionLayerProjection Projection; + XrCompositionLayerCylinderKHR Cylinder; +} ovrCompositorLayer_Union; + +typedef struct { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +} ovrSwapChain; + +typedef struct { + int Width; + int Height; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + ovrSwapChain ColorSwapChain; + ovrSwapChain DepthSwapChain; + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; + XrSwapchainImageOpenGLESKHR* DepthSwapChainImage; + unsigned int* FrameBuffers; + bool Acquired; +} ovrFramebuffer; + +typedef struct { + bool Multiview; + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; +} ovrRenderer; + +typedef struct { + int Focused; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace StageSpace; + XrSpace FakeStageSpace; + XrSpace CurrentSpace; + int SessionActive; + + int SwapInterval; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + + ovrRenderer Renderer; +} ovrApp; + +typedef struct { + JavaVM* Vm; + jobject ActivityObject; + JNIEnv* Env; + char AppName[64]; + int AppVersion; +} ovrJava; + +typedef struct { + uint64_t frameIndex; + ovrApp appState; + ovrJava java; + float predictedDisplayTime; +} engine_t; void VR_Init( ovrJava java ); void VR_Destroy( engine_t* engine ); @@ -8,3 +152,7 @@ void VR_EnterVR( engine_t* engine ); void VR_LeaveVR( engine_t* engine ); engine_t* VR_GetEngine( void ); + +void ovrApp_Clear(ovrApp* app); +void ovrApp_Destroy(ovrApp* app); +int ovrApp_HandleXrEvents(ovrApp* app); diff --git a/Common/VR/VRFramebuffer.cpp b/Common/VR/VRFramebuffer.cpp index be535e606c7c..70f2ad02aaf5 100644 --- a/Common/VR/VRFramebuffer.cpp +++ b/Common/VR/VRFramebuffer.cpp @@ -24,6 +24,7 @@ ovrFramebuffer ================================================================================ */ + void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->Width = 0; frameBuffer->Height = 0; @@ -33,87 +34,99 @@ void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->ColorSwapChain.Width = 0; frameBuffer->ColorSwapChain.Height = 0; frameBuffer->ColorSwapChainImage = NULL; - frameBuffer->DepthBuffers = NULL; + frameBuffer->DepthSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->DepthSwapChain.Width = 0; + frameBuffer->DepthSwapChain.Height = 0; + frameBuffer->DepthSwapChainImage = NULL; + frameBuffer->FrameBuffers = NULL; + frameBuffer->Acquired = false; } -bool ovrFramebuffer_Create( - XrSession session, - ovrFramebuffer* frameBuffer, - const int width, - const int height) { +bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int width, int height, bool multiview) { frameBuffer->Width = width; frameBuffer->Height = height; + if (strstr((const char*)glGetString(GL_EXTENSIONS), "GL_OVR_multiview2") == nullptr) + { + ALOGE("OpenGL implementation does not support GL_OVR_multiview2 extension.\n"); + } + + typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); + auto glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)eglGetProcAddress ("glFramebufferTextureMultiviewOVR"); + if (!glFramebufferTextureMultiviewOVR) + { + ALOGE("Can not get proc address for glFramebufferTextureMultiviewOVR.\n"); + } + XrSwapchainCreateInfo swapChainCreateInfo; memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo)); swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; - swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; - swapChainCreateInfo.format = GL_RGBA8; swapChainCreateInfo.sampleCount = 1; swapChainCreateInfo.width = width; swapChainCreateInfo.height = height; swapChainCreateInfo.faceCount = 1; - swapChainCreateInfo.arraySize = 1; swapChainCreateInfo.mipCount = 1; + swapChainCreateInfo.arraySize = multiview ? 2 : 1; frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + frameBuffer->DepthSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height; - // Create the swapchain. + // Create the color swapchain. + swapChainCreateInfo.format = GL_RGBA8; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); - // Get the number of swapchain images. - OXR(xrEnumerateSwapchainImages( - frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); - // Allocate the swapchain images array. - frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( - frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + OXR(xrEnumerateSwapchainImages(frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Create the depth swapchain. + swapChainCreateInfo.format = GL_DEPTH24_STENCIL8; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->DepthSwapChain.Handle)); + frameBuffer->DepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); // Populate the swapchain image array. for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; frameBuffer->ColorSwapChainImage[i].next = NULL; + frameBuffer->DepthSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->DepthSwapChainImage[i].next = NULL; } OXR(xrEnumerateSwapchainImages( frameBuffer->ColorSwapChain.Handle, frameBuffer->TextureSwapChainLength, &frameBuffer->TextureSwapChainLength, (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + OXR(xrEnumerateSwapchainImages( + frameBuffer->DepthSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->DepthSwapChainImage)); - frameBuffer->DepthBuffers = - (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); - frameBuffer->FrameBuffers = - (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); - + frameBuffer->FrameBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { - // Create the color buffer texture. const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; - GLenum colorTextureTarget = GL_TEXTURE_2D; - GL(glBindTexture(colorTextureTarget, colorTexture)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GL(glBindTexture(colorTextureTarget, 0)); - - // Create depth buffer. - GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); - GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height)); - GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + const GLuint depthTexture = frameBuffer->DepthSwapChainImage[i].image; // Create the frame buffer. GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); - GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + if (multiview) { + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexture, 0, 0, 2)); + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0, 2)); + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, 0, 2)); + } else { + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0)); + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0)); + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + } GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { - ALOGE( - "Incomplete frame buffer object: %d", renderFramebufferStatus); + ALOGE("Incomplete frame buffer object: %d", renderFramebufferStatus); return false; } } @@ -123,19 +136,17 @@ bool ovrFramebuffer_Create( void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); - GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + OXR(xrDestroySwapchain(frameBuffer->DepthSwapChain.Handle)); free(frameBuffer->ColorSwapChainImage); - - free(frameBuffer->DepthBuffers); + free(frameBuffer->DepthSwapChainImage); free(frameBuffer->FrameBuffers); ovrFramebuffer_Clear(frameBuffer); } void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) { - GL(glBindFramebuffer( - GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); } void ovrFramebuffer_SetNone() { @@ -149,18 +160,16 @@ void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) { } void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { - // Acquire the swapchain image XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; - OXR(xrAcquireSwapchainImage( - frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + OXR(xrAcquireSwapchainImage(frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); XrSwapchainImageWaitInfo waitInfo; waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO; waitInfo.next = NULL; - waitInfo.timeout = 1000; /* timeout in nanoseconds */ + waitInfo.timeout = 1000000; /* timeout in nanoseconds */ XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); int i = 0; - while (res != XR_SUCCESS) { + while ((res != XR_SUCCESS) && (i < 10)) { res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); i++; ALOGV( @@ -168,11 +177,15 @@ void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { i, waitInfo.timeout * (1E-9)); } + frameBuffer->Acquired = res == XR_SUCCESS; } void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) { - XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; - OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); + if (frameBuffer->Acquired) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); + frameBuffer->Acquired = false; + } } /* @@ -184,29 +197,23 @@ ovrRenderer */ void ovrRenderer_Clear(ovrRenderer* renderer) { - for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]); + for (int i = 0; i < ovrMaxNumEyes; i++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[i]); } } -void ovrRenderer_Create( - XrSession session, - ovrRenderer* renderer, - int suggestedEyeTextureWidth, - int suggestedEyeTextureHeight) { - // Create the frame buffers. - for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - ovrFramebuffer_Create( - session, - &renderer->FrameBuffer[eye], - suggestedEyeTextureWidth, - suggestedEyeTextureHeight); +void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview) { + renderer->Multiview = multiview; + int instances = renderer->Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer_Create(session, &renderer->FrameBuffer[i], width, height, multiview); } } void ovrRenderer_Destroy(ovrRenderer* renderer) { - for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); + int instances = renderer->Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[i]); } } @@ -235,7 +242,6 @@ void ovrApp_Clear(ovrApp* app) { app->LayerCount = 0; app->MainThreadTid = 0; app->RenderThreadTid = 0; - app->TouchPadDownLastFrame = false; ovrRenderer_Clear(&app->Renderer); } @@ -258,6 +264,32 @@ void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); app->SessionActive = (result == XR_SUCCESS); + + // Set session state once we have entered VR mode and have a valid session object. +#ifdef OPENXR_HAS_PERFORMANCE_EXTENSION + if (app->SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT(app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT(app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR(app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR(app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid)); + } +#endif } else if (state == XR_SESSION_STATE_STOPPING) { assert(app->SessionActive); diff --git a/Common/VR/VRFramebuffer.h b/Common/VR/VRFramebuffer.h index 90e14a20308b..dcedad580fd0 100644 --- a/Common/VR/VRFramebuffer.h +++ b/Common/VR/VRFramebuffer.h @@ -1,89 +1,6 @@ #pragma once -//OpenXR -#define XR_USE_GRAPHICS_API_OPENGL_ES 1 -#define XR_USE_PLATFORM_ANDROID 1 -#include -#include -#include -#include -#include -#include - -#define ALOGE(...) printf(__VA_ARGS__) -#define ALOGV(...) printf(__VA_ARGS__) - -typedef union { - XrCompositionLayerProjection Projection; - XrCompositionLayerCylinderKHR Cylinder; -} ovrCompositorLayer_Union; - -enum { ovrMaxLayerCount = 1 }; -enum { ovrMaxNumEyes = 2 }; - -#define GL(func) func; -#define OXR(func) func; - -typedef struct { - JavaVM* Vm; - jobject ActivityObject; - JNIEnv* Env; - char AppName[64]; - int AppVersion; -} ovrJava; - -typedef struct { - XrSwapchain Handle; - uint32_t Width; - uint32_t Height; -} ovrSwapChain; - -typedef struct { - int Width; - int Height; - uint32_t TextureSwapChainLength; - uint32_t TextureSwapChainIndex; - ovrSwapChain ColorSwapChain; - XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; - unsigned int* DepthBuffers; - unsigned int* FrameBuffers; -} ovrFramebuffer; - -typedef struct { - ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; -} ovrRenderer; - -typedef struct { - int Focused; - - XrInstance Instance; - XrSession Session; - XrViewConfigurationProperties ViewportConfig; - XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; - XrSystemId SystemId; - XrSpace HeadSpace; - XrSpace StageSpace; - XrSpace FakeStageSpace; - XrSpace CurrentSpace; - int SessionActive; - - int SwapInterval; - // These threads will be marked as performance threads. - int MainThreadTid; - int RenderThreadTid; - ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; - int LayerCount; - - int TouchPadDownLastFrame; - ovrRenderer Renderer; -} ovrApp; - -typedef struct { - uint64_t frameIndex; - ovrApp appState; - ovrJava java; - float predictedDisplayTime; -} engine_t; +#include "VR/VRBase.h" void ovrApp_Clear(ovrApp* app); void ovrApp_Destroy(ovrApp* app); @@ -95,9 +12,5 @@ void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer); void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer); void ovrFramebuffer_SetNone(); -void ovrRenderer_Create( - XrSession session, - ovrRenderer* renderer, - int suggestedEyeTextureWidth, - int suggestedEyeTextureHeight); +void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview); void ovrRenderer_Destroy(ovrRenderer* renderer); diff --git a/Common/VR/VRInput.cpp b/Common/VR/VRInput.cpp index 998547d26e47..63fc97a03032 100644 --- a/Common/VR/VRInput.cpp +++ b/Common/VR/VRInput.cpp @@ -25,7 +25,6 @@ XrAction vibrateRightFeedback; XrActionSet runningActionSet; XrSpace leftControllerAimSpace = XR_NULL_HANDLE; XrSpace rightControllerAimSpace = XR_NULL_HANDLE; -int actionsAttached = 0; int inputInitialized = 0; int useSimpleProfile = 0; @@ -348,6 +347,14 @@ void IN_VRInit( engine_t *engine ) { suggestedBindings.countSuggestedBindings = currBinding; OXR(xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings)); + // Attach actions + XrSessionActionSetsAttachInfo attachInfo = {}; + attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO; + attachInfo.next = NULL; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &runningActionSet; + OXR(xrAttachSessionActionSets(engine->appState.Session, &attachInfo)); + // Enumerate actions XrPath actionPathsBuffer[32]; char stringBuffer[256]; @@ -370,11 +377,11 @@ void IN_VRInit( engine_t *engine ) { handPoseLeftAction, handPoseRightAction }; - for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) { + for (XrAction & i : actionsToEnumerate) { XrBoundSourcesForActionEnumerateInfo enumerateInfo = {}; enumerateInfo.type = XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO; enumerateInfo.next = NULL; - enumerateInfo.action = actionsToEnumerate[i]; + enumerateInfo.action = i; // Get Count uint32_t countOutput = 0; @@ -425,16 +432,6 @@ void IN_VRInit( engine_t *engine ) { } void IN_VRInputFrame( engine_t* engine ) { - // Attach to session - if (!actionsAttached) { - XrSessionActionSetsAttachInfo attachInfo = {}; - attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO; - attachInfo.next = NULL; - attachInfo.countActionSets = 1; - attachInfo.actionSets = &runningActionSet; - OXR(xrAttachSessionActionSets(engine->appState.Session, &attachInfo)); - actionsAttached = 1; - } // sync action data XrActiveActionSet activeActionSet = {}; @@ -509,3 +506,12 @@ uint32_t IN_VRGetButtonState( int controllerIndex ) { XrVector2f IN_VRGetJoystickState( int controllerIndex ) { return moveJoystickState[controllerIndex].currentState; } + +XrPosef IN_VRGetPose( int controllerIndex ) { + engine_t* engine = VR_GetEngine(); + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + XrSpace aimSpace[] = { leftControllerAimSpace, rightControllerAimSpace }; + xrLocateSpace(aimSpace[controllerIndex], engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc); + return loc.pose; +} diff --git a/Common/VR/VRInput.h b/Common/VR/VRInput.h index 241bf2791e77..126cc56eaa4c 100644 --- a/Common/VR/VRInput.h +++ b/Common/VR/VRInput.h @@ -32,4 +32,5 @@ void IN_VRInit( engine_t *engine ); void IN_VRInputFrame( engine_t* engine ); uint32_t IN_VRGetButtonState( int controllerIndex ); XrVector2f IN_VRGetJoystickState( int controllerIndex ); +XrPosef IN_VRGetPose( int controllerIndex ); void INVR_Vibrate( int duration, int chan, float intensity ); diff --git a/Common/VR/VRMath.h b/Common/VR/VRMath.h index c0ccb36aacd3..eb801ac73fa8 100644 --- a/Common/VR/VRMath.h +++ b/Common/VR/VRMath.h @@ -17,7 +17,7 @@ float ToRadians(float deg); // ovrMatrix4f float ovrMatrix4f_Minor(const ovrMatrix4f* m, int r0, int r1, int r2, int c0, int c1, int c2); ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q); -ovrMatrix4f ovrMatrix4f_CreateProjectionFov(const float fovDegreesX, const float fovDegreesY, const float offsetX, const float offsetY, const float nearZ, const float farZ); +ovrMatrix4f ovrMatrix4f_CreateProjectionFov(const float angleLeft, const float angleRight, const float angleUp, const float angleDown, const float nearZ, const float farZ); ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ); ovrMatrix4f ovrMatrix4f_Inverse(const ovrMatrix4f* m); ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b); diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index 01871b8acb36..1b42a27adbea 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -16,9 +16,6 @@ GLboolean initialized = GL_FALSE; GLboolean stageSupported = GL_FALSE; int vrConfig[VR_CONFIG_MAX] = {}; -float menuPitch = 0; -float menuYaw = 0; -float recenterYaw = 0; XrVector3f hmdorientation; XrVector3f hmdposition; @@ -26,8 +23,7 @@ void VR_UpdateStageBounds(ovrApp* pappState) { XrExtent2Df stageBounds = {}; XrResult result; - OXR(result = xrGetReferenceSpaceBoundsRect( - pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + OXR(result = xrGetReferenceSpaceBoundsRect(pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); if (result != XR_SUCCESS) { ALOGV("Stage bounds query failed: using small defaults"); stageBounds.width = 1.0f; @@ -136,7 +132,8 @@ void VR_Recenter(engine_t* engine) { OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc)); hmdorientation = XrQuaternionf_ToEulerAngles(loc.pose.orientation); - recenterYaw += ToRadians(hmdorientation.y); + vrConfig[VR_CONFIG_RECENTER_YAW] += (int)hmdorientation.y; + float recenterYaw = ToRadians((float)vrConfig[VR_CONFIG_RECENTER_YAW]); spaceCreateInfo.poseInReferenceSpace.orientation.x = 0; spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(recenterYaw / 2); spaceCreateInfo.poseInReferenceSpace.orientation.z = 0; @@ -172,32 +169,28 @@ void VR_Recenter(engine_t* engine) { } // Update menu orientation - menuPitch = hmdorientation.x; - menuYaw = 0; + vrConfig[VR_CONFIG_MENU_PITCH] = (int)hmdorientation.x; + vrConfig[VR_CONFIG_MENU_YAW] = 0; } -void VR_InitRenderer( engine_t* engine ) { +void VR_InitRenderer( engine_t* engine, bool multiview ) { if (initialized) { VR_DestroyRenderer(engine); } int eyeW, eyeH; VR_GetResolution(engine, &eyeW, &eyeH); + vrConfig[VR_CONFIG_VIEWPORT_WIDTH] = eyeW; + vrConfig[VR_CONFIG_VIEWPORT_HEIGHT] = eyeH; // Get the viewport configuration info for the chosen viewport configuration type. engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; - - OXR(xrGetViewConfigurationProperties( - engine->appState.Instance, engine->appState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &engine->appState.ViewportConfig)); + OXR(xrGetViewConfigurationProperties(engine->appState.Instance, engine->appState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &engine->appState.ViewportConfig)); uint32_t numOutputSpaces = 0; OXR(xrEnumerateReferenceSpaces(engine->appState.Session, 0, &numOutputSpaces, NULL)); - - XrReferenceSpaceType* referenceSpaces = - (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); - - OXR(xrEnumerateReferenceSpaces( - engine->appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + XrReferenceSpaceType* referenceSpaces = (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); + OXR(xrEnumerateReferenceSpaces(engine->appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); for (uint32_t i = 0; i < numOutputSpaces; i++) { if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { @@ -218,7 +211,8 @@ void VR_InitRenderer( engine_t* engine ) { engine->appState.Session, &engine->appState.Renderer, engine->appState.ViewConfigurationView[0].recommendedImageRectWidth, - engine->appState.ViewConfigurationView[0].recommendedImageRectHeight); + engine->appState.ViewConfigurationView[0].recommendedImageRectHeight, + multiview); initialized = GL_TRUE; } @@ -241,13 +235,13 @@ void VR_ClearFrameBuffer( int width, int height) { glDisable( GL_SCISSOR_TEST ); } -void VR_BeginFrame( engine_t* engine ) { +bool VR_InitFrame( engine_t* engine ) { GLboolean stageBoundsDirty = GL_TRUE; if (ovrApp_HandleXrEvents(&engine->appState)) { VR_Recenter(engine); } if (engine->appState.SessionActive == GL_FALSE) { - return; + return false; } if (stageBoundsDirty) { @@ -267,7 +261,7 @@ void VR_BeginFrame( engine_t* engine ) { OXR(xrWaitFrame(engine->appState.Session, &waitFrameInfo, &frameState)); engine->predictedDisplayTime = frameState.predictedDisplayTime; if (!frameState.shouldRender) { - return; + return false; } // Get the HMD pose, predicted for the middle of the time period during which @@ -310,43 +304,60 @@ void VR_BeginFrame( engine_t* engine ) { engine->appState.LayerCount = 0; memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + return true; +} - for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; - - ovrFramebuffer_Acquire(frameBuffer); - ovrFramebuffer_SetCurrent(frameBuffer); - VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); - } +void VR_BeginFrame( engine_t* engine, int fboIndex ) { + vrConfig[VR_CONFIG_CURRENT_FBO] = fboIndex; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; + ovrFramebuffer_Acquire(frameBuffer); + ovrFramebuffer_SetCurrent(frameBuffer); + VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); } void VR_EndFrame( engine_t* engine ) { - for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - - // Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - glClearColor(0.0, 0.0, 0.0, 1.0); + int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO]; + + // Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully + VR_BindFramebuffer(engine); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // Show mouse cursor + int size = vrConfig[VR_CONFIG_MOUSE_SIZE]; + if ((vrConfig[VR_CONFIG_MODE] == VR_MODE_FLAT_SCREEN) && (size > 0)) { + glEnable(GL_SCISSOR_TEST); + glScissor(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); + glViewport(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); + glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; - //TODO:ovrFramebuffer_Resolve(frameBuffer); - ovrFramebuffer_Release(frameBuffer); + glDisable(GL_SCISSOR_TEST); } + + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; + //ovrFramebuffer_Resolve(frameBuffer); + ovrFramebuffer_Release(frameBuffer); ovrFramebuffer_SetNone(); +} + +void VR_FinishFrame( engine_t* engine ) { - XrCompositionLayerProjectionView projection_layer_elements[2] = {}; int vrMode = vrConfig[VR_CONFIG_MODE]; + XrCompositionLayerProjectionView projection_layer_elements[2] = {}; if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) { - menuYaw = hmdorientation.y; + vrConfig[VR_CONFIG_MENU_YAW] = (int)hmdorientation.y; for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + int imageLayer = engine->appState.Renderer.Multiview ? eye : 0; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[0]; XrFovf fov = projections[eye].fov; - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; if (vrMode == VR_MODE_MONO_6DOF) { - frameBuffer = &engine->appState.Renderer.FrameBuffer[0]; fov = projections[0].fov; + } else if (!engine->appState.Renderer.Multiview) { + frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; } memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); @@ -360,7 +371,7 @@ void VR_EndFrame( engine_t* engine ) { projection_layer_elements[eye].subImage.imageRect.offset.y = 0; projection_layer_elements[eye].subImage.imageRect.extent.width = frameBuffer->ColorSwapChain.Width; projection_layer_elements[eye].subImage.imageRect.extent.height = frameBuffer->ColorSwapChain.Height; - projection_layer_elements[eye].subImage.imageArrayIndex = 0; + projection_layer_elements[eye].subImage.imageArrayIndex = imageLayer; } XrCompositionLayerProjection projection_layer = {}; @@ -389,14 +400,16 @@ void VR_EndFrame( engine_t* engine ) { cylinder_layer.subImage.imageRect.extent.width = width; cylinder_layer.subImage.imageRect.extent.height = height; cylinder_layer.subImage.imageArrayIndex = 0; - float distance = vrConfig[VR_CONFIG_CANVAS_DISTANCE]; + float distance = (float)vrConfig[VR_CONFIG_CANVAS_DISTANCE]; + float menuPitch = ToRadians((float)vrConfig[VR_CONFIG_MENU_PITCH]); + float menuYaw = ToRadians((float)vrConfig[VR_CONFIG_MENU_YAW]); XrVector3f pos = { - invViewTransform[0].position.x - sin(ToRadians(menuYaw)) * distance, + invViewTransform[0].position.x - sin(menuYaw) * distance, invViewTransform[0].position.y, - invViewTransform[0].position.z - cos(ToRadians(menuYaw)) * distance + invViewTransform[0].position.z - cos(menuYaw) * distance }; - XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -ToRadians(menuPitch)); - XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, ToRadians(menuYaw)); + XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -menuPitch); + XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, menuYaw); cylinder_layer.pose.orientation = XrQuaternionf_Multiply(pitch, yaw); cylinder_layer.pose.position = pos; cylinder_layer.radius = 12.0f; @@ -422,8 +435,9 @@ void VR_EndFrame( engine_t* engine ) { endFrameInfo.layers = layers; OXR(xrEndFrame(engine->appState.Session, &endFrameInfo)); - for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; + int instances = engine->appState.Renderer.Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[instances]; frameBuffer->TextureSwapChainIndex++; frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength; } @@ -437,29 +451,23 @@ void VR_SetConfig( VRConfig config, int value) { vrConfig[config] = value; } -void VR_BindFramebuffer( engine_t* engine, int eye ) { +void VR_BindFramebuffer(engine_t *engine) { if (!initialized) return; - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; - int swapchainIndex = frameBuffer->TextureSwapChainIndex; - int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex]; - glBindFramebuffer(GL_FRAMEBUFFER, glFramebuffer); + int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO]; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; + unsigned int swapchainIndex = frameBuffer->TextureSwapChainIndex; + unsigned int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex]; + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glFramebuffer)); } ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) { ovrMatrix4f output; - if (matrix == VR_PROJECTION_MATRIX_HUD) { - float hudScale = ToRadians(15.0f); - output = ovrMatrix4f_CreateProjectionFov(-hudScale, hudScale, hudScale, -hudScale, 1.0f, 0.0f ); - } else if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) { + if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) { XrFovf fov = matrix == VR_PROJECTION_MATRIX_LEFT_EYE ? projections[0].fov : projections[1].fov; - float fovScale = vrConfig[VR_CONFIG_FOV_SCALE] * 0.01f; - fov.angleLeft *= fovScale; - fov.angleRight *= fovScale; - fov.angleUp *= fovScale; - fov.angleDown *= fovScale; - output = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, 1.0f, 0.0f ); + float near = (float)vrConfig[VR_CONFIG_FOV_SCALE] / 200.0f; + output = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, near, 0.0f ); } else if ((matrix == VR_VIEW_MATRIX_LEFT_EYE) || (matrix == VR_VIEW_MATRIX_RIGHT_EYE)) { - XrPosef invView = matrix == VR_VIEW_MATRIX_LEFT_EYE ? invViewTransform[0] : invViewTransform[1]; + XrPosef invView = invViewTransform[0]; // get axis mirroring configuration float mx = vrConfig[VR_CONFIG_MIRROR_PITCH] ? -1 : 1; @@ -485,12 +493,18 @@ ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) { } output = ovrMatrix4f_CreateFromQuaternion(&invView.orientation); + float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.001f; if (vrConfig[VR_CONFIG_6DOF_ENABLED]) { - float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.001f; output.M[0][3] -= hmdposition.x * (vrConfig[VR_CONFIG_MIRROR_AXIS_X] ? -1.0f : 1.0f) * scale; output.M[1][3] -= hmdposition.y * (vrConfig[VR_CONFIG_MIRROR_AXIS_Y] ? -1.0f : 1.0f) * scale; output.M[2][3] -= hmdposition.z * (vrConfig[VR_CONFIG_MIRROR_AXIS_Z] ? -1.0f : 1.0f) * scale; } + if (matrix == VR_VIEW_MATRIX_RIGHT_EYE) { + float ipdScale = (float)vrConfig[VR_CONFIG_STEREO_SEPARATION] * 0.1f * scale; + output.M[0][3] += (invViewTransform[1].position.x - invViewTransform[0].position.x) * ipdScale; + output.M[1][3] += (invViewTransform[1].position.y - invViewTransform[0].position.y) * ipdScale; + output.M[2][3] += (invViewTransform[1].position.z - invViewTransform[0].position.z) * ipdScale; + } } else { assert(false); } diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 721342252ce5..a8015776fde0 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -4,28 +4,32 @@ #include "VRMath.h" enum VRConfig { - VR_CONFIG_MODE, - VR_CONFIG_6DOF_ENABLED, - VR_CONFIG_6DOF_SCALE, - VR_CONFIG_MIRROR_AXIS_X, - VR_CONFIG_MIRROR_AXIS_Y, - VR_CONFIG_MIRROR_AXIS_Z, - VR_CONFIG_MIRROR_PITCH, - VR_CONFIG_MIRROR_YAW, - VR_CONFIG_MIRROR_ROLL, - VR_CONFIG_3D_GEOMETRY_COUNT, - VR_CONFIG_FOV_SCALE, - VR_CONFIG_FORCE_2D, - VR_CONFIG_CANVAS_DISTANCE, + //switching between 2D and 3D + VR_CONFIG_MODE, VR_CONFIG_3D_GEOMETRY_COUNT, VR_CONFIG_FORCE_2D, + //camera setup + VR_CONFIG_FOV_SCALE, VR_CONFIG_CANVAS_DISTANCE, VR_CONFIG_STEREO_SEPARATION, + //6DoF + VR_CONFIG_6DOF_ENABLED, VR_CONFIG_6DOF_SCALE, + VR_CONFIG_MIRROR_AXIS_X, VR_CONFIG_MIRROR_AXIS_Y, VR_CONFIG_MIRROR_AXIS_Z, + VR_CONFIG_MIRROR_PITCH, VR_CONFIG_MIRROR_YAW, VR_CONFIG_MIRROR_ROLL, + //2D canvas positioning + VR_CONFIG_MENU_PITCH, VR_CONFIG_MENU_YAW, VR_CONFIG_RECENTER_YAW, + //mouse cursor + VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y, + //viewport setup + VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, VR_CONFIG_VIEWPORT_VALID, + //render status + VR_CONFIG_CURRENT_FBO, + + //end VR_CONFIG_MAX }; enum VRMatrix { - VR_PROJECTION_MATRIX_HUD = 0, - VR_PROJECTION_MATRIX_LEFT_EYE = 1, - VR_PROJECTION_MATRIX_RIGHT_EYE = 2, - VR_VIEW_MATRIX_LEFT_EYE = 3, - VR_VIEW_MATRIX_RIGHT_EYE = 4 + VR_PROJECTION_MATRIX_LEFT_EYE = 0, + VR_PROJECTION_MATRIX_RIGHT_EYE = 1, + VR_VIEW_MATRIX_LEFT_EYE = 2, + VR_VIEW_MATRIX_RIGHT_EYE = 3 }; enum VRMode { @@ -35,14 +39,16 @@ enum VRMode { }; void VR_GetResolution( engine_t* engine, int *pWidth, int *pHeight ); -void VR_InitRenderer( engine_t* engine ); +void VR_InitRenderer( engine_t* engine, bool multiview ); void VR_DestroyRenderer( engine_t* engine ); -void VR_BeginFrame( engine_t* engine ); +bool VR_InitFrame( engine_t* engine ); +void VR_BeginFrame( engine_t* engine, int fboIndex ); void VR_EndFrame( engine_t* engine ); +void VR_FinishFrame( engine_t* engine ); int VR_GetConfig( VRConfig config ); void VR_SetConfig( VRConfig config, int value); -void VR_BindFramebuffer( engine_t* engine, int eye ); +void VR_BindFramebuffer(engine_t *engine); ovrMatrix4f VR_GetMatrix( VRMatrix matrix ); diff --git a/Common/VR/VRTweaks.cpp b/Common/VR/VRTweaks.cpp index 7332bb734ea6..edd21b45f461 100644 --- a/Common/VR/VRTweaks.cpp +++ b/Common/VR/VRTweaks.cpp @@ -68,10 +68,15 @@ void VR_TweakMirroring(float* projMatrix) { } void VR_TweakProjection(float* src, float* dst, VRMatrix matrix) { - memcpy(dst, src, 16 * sizeof(float)); ovrMatrix4f hmdProjection = VR_GetMatrix(matrix); - dst[0] = (dst[0] > 0 ? 1.0f : -1.0f) * hmdProjection.M[0][0]; - dst[5] = (dst[5] > 0 ? 1.0f : -1.0f) * hmdProjection.M[1][1]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + if ((hmdProjection.M[i][j] > 0) != (src[i * 4 + j] > 0)) { + hmdProjection.M[i][j] *= -1.0f; + } + } + } + memcpy(dst, hmdProjection.M, 16 * sizeof(float)); } void VR_TweakView(float* view, float* projMatrix, VRMatrix matrix) { diff --git a/Core/Config.cpp b/Core/Config.cpp index 54216c31cdbd..710dd28af398 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -44,6 +44,7 @@ #include "Common/System/System.h" #include "Common/StringUtils.h" #include "Common/GPU/Vulkan/VulkanLoader.h" +#include "Common/VR/PPSSPPVR.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Loaders.h" @@ -695,9 +696,9 @@ const char * const vulkanDefaultBlacklist[] = { }; static int DefaultGPUBackend() { -#ifdef OPENXR - return (int)GPUBackend::OPENGL; -#endif + if (IsVRBuild()) { + return (int)GPUBackend::OPENGL; + } #if PPSSPP_PLATFORM(WINDOWS) // If no Vulkan, use Direct3D 11 on Windows 8+ (most importantly 10.) @@ -1204,8 +1205,10 @@ static ConfigSetting themeSettings[] = { static ConfigSetting vrSettings[] = { ConfigSetting("VREnable", &g_Config.bEnableVR, true), ConfigSetting("VREnable6DoF", &g_Config.bEnable6DoF, true), + ConfigSetting("VREnableStereo", &g_Config.bEnableStereo, false), ConfigSetting("VRCanvasDistance", &g_Config.iCanvasDistance, 6), ConfigSetting("VRFieldOfView", &g_Config.iFieldOfViewPercentage, 100), + ConfigSetting("VRStereoSeparation", &g_Config.iStereoSeparation, 10), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index 16de8394bdab..433e838f3286 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -455,8 +455,10 @@ struct Config { // Virtual reality bool bEnableVR; bool bEnable6DoF; + bool bEnableStereo; int iCanvasDistance; int iFieldOfViewPercentage; + int iStereoSeparation; // Debugger int iDisasmWindowX; diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index 2a4cf3f458fe..b0a3b69c7e27 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -25,6 +25,7 @@ #include "Common/System/System.h" #include "Common/Data/Format/IniFile.h" #include "Common/Input/InputState.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/Log.h" #include "Common/StringUtils.h" #include "Core/HLE/sceUtility.h" @@ -654,10 +655,11 @@ void SetAxisMapping(int btn, int deviceId, int axisId, int direction, bool repla void RestoreDefault() { g_controllerMap.clear(); g_controllerMapGeneration++; -#ifdef OPENXR - SetDefaultKeyMap(DEFAULT_MAPPING_VR_HEADSET, false); - return; -#endif + + if (IsVRBuild()) { + SetDefaultKeyMap(DEFAULT_MAPPING_VR_HEADSET, false); + return; + } #if PPSSPP_PLATFORM(WINDOWS) SetDefaultKeyMap(DEFAULT_MAPPING_KEYBOARD, true); diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 1603234ad0ba..552e62a0c5bb 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -24,6 +24,7 @@ #include "Common/System/Display.h" #include "Common/System/System.h" #include "Common/File/VFS/VFS.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/Log.h" #include "Common/TimeUtil.h" #include "Core/Config.h" @@ -35,10 +36,6 @@ #include "GPU/Common/PresentationCommon.h" #include "Common/GPU/ShaderTranslation.h" -#ifdef OPENXR -#include "VR/VRRenderer.h" -#endif - struct Vertex { float x, y, z; float u, v; @@ -77,13 +74,13 @@ void CenterDisplayOutputRect(FRect *rc, float origW, float origH, const FRect &f bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180; -#ifdef OPENXR - if (VR_GetConfig(VR_CONFIG_MODE) == VR_MODE_FLAT_SCREEN) { - g_Config.iSmallDisplayZoomType = (int)SmallDisplayZoom::AUTO; - } else { - g_Config.iSmallDisplayZoomType = (int)SmallDisplayZoom::STRETCH; + if (IsVRBuild()) { + if (IsFlatVRScene()) { + g_Config.iSmallDisplayZoomType = (int)SmallDisplayZoom::AUTO; + } else { + g_Config.iSmallDisplayZoomType = (int)SmallDisplayZoom::STRETCH; + } } -#endif if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::STRETCH) { outW = frame.w; diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index 4e3a03e8ba90..071f1f4f5b04 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -23,6 +23,7 @@ #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/GPU/ShaderWriter.h" #include "Common/GPU/thin3d.h" +#include "Common/VR/PPSSPPVR.h" #include "Core/Config.h" #include "GPU/ge_constants.h" #include "GPU/GPUState.h" @@ -151,6 +152,9 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag gl_exts.push_back("#extension GL_ARB_cull_distance : enable"); } } + if (IsVRBuild() && IsMultiviewSupported()) { + gl_exts.push_back("#extension GL_OVR_multiview2 : enable\nlayout(num_views=2) in;"); + } ShaderWriter p(buffer, compat, ShaderStage::Vertex, gl_exts.data(), gl_exts.size()); bool isModeThrough = id.Bit(VS_BIT_IS_THROUGH); @@ -469,6 +473,13 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "uniform mat4 u_proj_through;\n"); *uniformMask |= DIRTY_PROJTHROUGHMATRIX; } else if (useHWTransform) { + if (IsVRBuild()) { + if (IsMultiviewSupported()) { + WRITE(p, "layout(shared) uniform ProjectionMatrix { uniform mat4 u_proj_lens[2]; };\n"); + } else { + WRITE(p, "uniform mat4 u_proj_lens;\n"); + } + } WRITE(p, "uniform mat4 u_proj;\n"); *uniformMask |= DIRTY_PROJMATRIX; } @@ -477,7 +488,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // When transforming by hardware, we need a great deal more uniforms... // TODO: Use 4x3 matrices where possible. Though probably doesn't matter much. WRITE(p, "uniform mat4 u_world;\n"); - WRITE(p, "uniform mat4 u_view;\n"); + if (IsVRBuild() && IsMultiviewSupported()) { + WRITE(p, "layout(shared) uniform ViewMatrices { uniform mat4 u_view[2]; };\n"); + } else { + WRITE(p, "uniform mat4 u_view;\n"); + } *uniformMask |= DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX; if (doTextureTransform) { WRITE(p, "uniform mediump mat4 u_texmtx;\n"); @@ -534,10 +549,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "uniform lowp float u_rotation;\n"); } -#ifdef OPENXR - WRITE(p, "uniform lowp float u_scaleX;\n"); - WRITE(p, "uniform lowp float u_scaleY;\n"); -#endif + if (IsVRBuild()) { + WRITE(p, "uniform lowp float u_scaleX;\n"); + WRITE(p, "uniform lowp float u_scaleY;\n"); + } if (useHWTransform || !hasColor) { WRITE(p, "uniform lowp vec4 u_matambientalpha;\n"); // matambient + matalpha @@ -900,13 +915,28 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " mediump vec3 worldnormal = normalizeOr001(mul(vec4(skinnednormal, 0.0), u_world).xyz);\n"); } - WRITE(p, " vec4 viewPos = vec4(mul(vec4(worldpos, 1.0), u_view).xyz, 1.0);\n"); + std::string matrixPostfix; + if (IsVRBuild() && IsMultiviewSupported()) { + matrixPostfix = "[gl_ViewID_OVR]"; + } + + WRITE(p, " vec4 viewPos = vec4(mul(vec4(worldpos, 1.0), u_view%s).xyz, 1.0);\n", matrixPostfix.c_str()); // Final view and projection transforms. if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj, viewPos));\n"); + if (IsVRBuild()) { + WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj_lens%s, viewPos));\n", matrixPostfix.c_str()); + WRITE(p, " vec4 orgPos = depthRoundZVP(mul(u_proj, viewPos));\n"); + } else { + WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj, viewPos));\n"); + } } else { - WRITE(p, " vec4 outPos = mul(u_proj, viewPos);\n"); + if (IsVRBuild()) { + WRITE(p, " vec4 outPos = mul(u_proj_lens%s, viewPos);\n", matrixPostfix.c_str()); + WRITE(p, " vec4 orgPos = mul(u_proj, viewPos);\n"); + } else { + WRITE(p, " vec4 outPos = mul(u_proj, viewPos);\n"); + } } // TODO: Declare variables for dots for shade mapping if needed. @@ -1144,7 +1174,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " %sv_fogdepth = (viewPos.z + u_fogcoef.x) * u_fogcoef.y;\n", compat.vsOutPrefix); } - if (vertexRangeCulling) { + if (vertexRangeCulling && !IsVRBuild()) { WRITE(p, " vec3 projPos = outPos.xyz / outPos.w;\n"); WRITE(p, " float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); // Vertex range culling doesn't happen when Z clips, note sign of w is important. @@ -1183,17 +1213,24 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // We've named the output gl_Position in HLSL as well. WRITE(p, " %sgl_Position = outPos;\n", compat.vsOutPrefix); + if (IsVRBuild()) { + // Z correction for the depth buffer + if (useHWTransform) { + WRITE(p, " %sgl_Position.z = orgPos.z / abs(orgPos.w) * abs(outPos.w);\n", compat.vsOutPrefix); + } + + // HUD scaling + WRITE(p, " if ((u_scaleX < 0.99) || (u_scaleY < 0.99)) {\n"); + WRITE(p, " %sgl_Position.x *= u_scaleX;\n", compat.vsOutPrefix); + WRITE(p, " %sgl_Position.y *= u_scaleY;\n", compat.vsOutPrefix); + WRITE(p, " }\n"); + } + if (needsZWHack) { // See comment in thin3d_vulkan.cpp. WRITE(p, " if (%sgl_Position.z == %sgl_Position.w) %sgl_Position.z *= 0.999999;\n", compat.vsOutPrefix, compat.vsOutPrefix, compat.vsOutPrefix); } -#ifdef OPENXR - WRITE(p, " if ((u_scaleX < 0.99) || (u_scaleY < 0.99)) {\n"); - WRITE(p, " %sgl_Position.x *= u_scaleX;\n", compat.vsOutPrefix); - WRITE(p, " %sgl_Position.y *= u_scaleY;\n", compat.vsOutPrefix); - WRITE(p, " }\n"); -#endif if (compat.shaderLanguage == HLSL_D3D11 || compat.shaderLanguage == HLSL_D3D9) { WRITE(p, " return Out;\n"); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 057bf91d4ffe..a1ae7edb6111 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -34,6 +34,7 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/OpenGL/GLRenderManager.h" #include "Common/System/Display.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/Log.h" #include "Common/File/FileUtil.h" @@ -50,12 +51,6 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "GPU/GLES/FramebufferManagerGLES.h" -#ifdef OPENXR -#include "VR/VRBase.h" -#include "VR/VRRenderer.h" -#include "VR/VRTweaks.h" -#endif - using namespace Lin; Shader::Shader(GLRenderManager *render, const char *code, const std::string &desc, const ShaderDescGLES ¶ms) @@ -107,6 +102,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_fbotex, "fbotex" }); queries.push_back({ &u_proj, "u_proj" }); + queries.push_back({ &u_proj_lens, "u_proj_lens" }); queries.push_back({ &u_proj_through, "u_proj_through" }); queries.push_back({ &u_texenv, "u_texenv" }); queries.push_back({ &u_fogcolor, "u_fogcolor" }); @@ -132,10 +128,11 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_cullRangeMin, "u_cullRangeMin" }); queries.push_back({ &u_cullRangeMax, "u_cullRangeMax" }); queries.push_back({ &u_rotation, "u_rotation" }); -#ifdef OPENXR - queries.push_back({ &u_scaleX, "u_scaleX" }); - queries.push_back({ &u_scaleY, "u_scaleY" }); -#endif + + if (IsVRBuild()) { + queries.push_back({ &u_scaleX, "u_scaleX" }); + queries.push_back({ &u_scaleY, "u_scaleY" }); + } #ifdef USE_BONE_ARRAY queries.push_back({ &u_bone, "u_bone" }); @@ -296,6 +293,56 @@ static inline void ScaleProjMatrix(Matrix4x4 &in, bool useBufferedRendering) { in.translateAndScale(trans, scale); } +static inline void FlipProjMatrix(Matrix4x4 &in, bool useBufferedRendering) { + + const bool invertedY = useBufferedRendering ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0); + if (invertedY) { + in[1] = -in[1]; + in[5] = -in[5]; + in[9] = -in[9]; + in[13] = -in[13]; + } + const bool invertedX = gstate_c.vpWidth < 0; + if (invertedX) { + in[0] = -in[0]; + in[4] = -in[4]; + in[8] = -in[8]; + in[12] = -in[12]; + } + + // In Phantasy Star Portable 2, depth range sometimes goes negative and is clamped by glDepthRange to 0, + // causing graphics clipping glitch (issue #1788). This hack modifies the projection matrix to work around it. + if (gstate_c.Supports(GPU_USE_DEPTH_RANGE_HACK)) { + float zScale = gstate.getViewportZScale() / 65535.0f; + float zCenter = gstate.getViewportZCenter() / 65535.0f; + + // if far depth range < 0 + if (zCenter + zScale < 0.0f) { + // if perspective projection + if (in[11] < 0.0f) { + float depthMax = gstate.getDepthRangeMax() / 65535.0f; + float depthMin = gstate.getDepthRangeMin() / 65535.0f; + + float a = in[10]; + float b = in[14]; + + float n = b / (a - 1.0f); + float f = b / (a + 1.0f); + + f = (n * f) / (n + ((zCenter + zScale) * (n - f) / (depthMax - depthMin))); + + a = (n + f) / (n - f); + b = (2.0f * n * f) / (n - f); + + if (!my_isnan(a) && !my_isnan(b)) { + in[10] = a; + in[14] = b; + } + } + } + } +} + void LinkedShader::use(const ShaderID &VSID) { render_->BindProgram(program); // Note that we no longer track attr masks here - we do it for the input layouts instead. @@ -304,9 +351,10 @@ void LinkedShader::use(const ShaderID &VSID) { void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBufferedRendering) { u64 dirty = dirtyUniforms & availableUniforms; dirtyUniforms = 0; -#ifdef OPENXR - dirty |= DIRTY_VIEWMATRIX; -#endif + + if (IsVRBuild()) { + dirty |= DIRTY_VIEWMATRIX; + } if (!dirty) return; @@ -321,96 +369,52 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu render_->SetUniformUI1(&u_depal_mask_shift_off_fmt, val); } -#ifdef OPENXR - // Count 3D instances - bool is2D = VR_TweakIsMatrixBigScale(gstate.projMatrix) || - VR_TweakIsMatrixIdentity(gstate.projMatrix) || - VR_TweakIsMatrixOneOrtho(gstate.projMatrix) || - VR_TweakIsMatrixOneScale(gstate.projMatrix) || - VR_TweakIsMatrixOneTransform(gstate.projMatrix); - if (!is2D && !gstate.isModeThrough()) { - VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) + 1); - } - - // Set HUD mode - bool is3D = gstate.isDepthWriteEnabled(); - bool flatScreen = VR_GetConfig(VR_CONFIG_MODE) == VR_MODE_FLAT_SCREEN; - bool hud = is2D && !is3D && !flatScreen && - gstate.isModeThrough() && //2D content requires orthographic projection - gstate.isAlphaBlendEnabled() && //2D content has to be blended - !gstate.isLightingEnabled() && //2D content cannot be rendered with lights on - !gstate.isFogEnabled(); //2D content cannot be rendered with fog on - if (hud) { - float scale = 0.5f; - render_->SetUniformF1(&u_scaleX, scale); - render_->SetUniformF1(&u_scaleY, scale / 480.0f * 272.0f); - } else { - render_->SetUniformF1(&u_scaleX, 1.0f); - render_->SetUniformF1(&u_scaleY, 1.0f); + bool is2D, flatScreen; + if (IsVRBuild()) { + // Analyze scene + is2D = Is2DVRObject(gstate.projMatrix, gstate.isModeThrough()); + flatScreen = IsFlatVRScene(); + + // Set HUD mode + bool is3D = gstate.isDepthWriteEnabled(); + bool hud = is2D && !is3D && !flatScreen && + gstate.isModeThrough() && //2D content requires orthographic projection + gstate.isAlphaBlendEnabled() && //2D content has to be blended + !gstate.isLightingEnabled() && //2D content cannot be rendered with lights on + !gstate.isFogEnabled(); //2D content cannot be rendered with fog on + if (hud) { + float scale = 0.5f; + render_->SetUniformF1(&u_scaleX, scale); + render_->SetUniformF1(&u_scaleY, scale / 480.0f * 272.0f); + } else { + render_->SetUniformF1(&u_scaleX, 1.0f); + render_->SetUniformF1(&u_scaleY, 1.0f); + } } -#endif // Update any dirty uniforms before we draw if (dirty & DIRTY_PROJMATRIX) { - Matrix4x4 flippedMatrix; -#ifdef OPENXR - if (flatScreen || is2D) { - memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); - } else { - VR_TweakProjection(gstate.projMatrix, flippedMatrix.m, VR_PROJECTION_MATRIX_LEFT_EYE); - VR_TweakMirroring(gstate.projMatrix); - } -#else - memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); -#endif - - const bool invertedY = useBufferedRendering ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0); - if (invertedY) { - flippedMatrix[1] = -flippedMatrix[1]; - flippedMatrix[5] = -flippedMatrix[5]; - flippedMatrix[9] = -flippedMatrix[9]; - flippedMatrix[13] = -flippedMatrix[13]; - } - const bool invertedX = gstate_c.vpWidth < 0; - if (invertedX) { - flippedMatrix[0] = -flippedMatrix[0]; - flippedMatrix[4] = -flippedMatrix[4]; - flippedMatrix[8] = -flippedMatrix[8]; - flippedMatrix[12] = -flippedMatrix[12]; - } - - // In Phantasy Star Portable 2, depth range sometimes goes negative and is clamped by glDepthRange to 0, - // causing graphics clipping glitch (issue #1788). This hack modifies the projection matrix to work around it. - if (gstate_c.Supports(GPU_USE_DEPTH_RANGE_HACK)) { - float zScale = gstate.getViewportZScale() / 65535.0f; - float zCenter = gstate.getViewportZCenter() / 65535.0f; - - // if far depth range < 0 - if (zCenter + zScale < 0.0f) { - // if perspective projection - if (flippedMatrix[11] < 0.0f) { - float depthMax = gstate.getDepthRangeMax() / 65535.0f; - float depthMin = gstate.getDepthRangeMin() / 65535.0f; - - float a = flippedMatrix[10]; - float b = flippedMatrix[14]; - - float n = b / (a - 1.0f); - float f = b / (a + 1.0f); - - f = (n * f) / (n + ((zCenter + zScale) * (n - f) / (depthMax - depthMin))); + if (IsVRBuild()) { + Matrix4x4 leftEyeMatrix, rightEyeMatrix; + if (flatScreen || is2D) { + memcpy(&leftEyeMatrix, gstate.projMatrix, 16 * sizeof(float)); + memcpy(&rightEyeMatrix, gstate.projMatrix, 16 * sizeof(float)); + } else { + UpdateVRProjection(gstate.projMatrix, leftEyeMatrix.m, rightEyeMatrix.m); + } - a = (n + f) / (n - f); - b = (2.0f * n * f) / (n - f); + FlipProjMatrix(leftEyeMatrix, useBufferedRendering); + FlipProjMatrix(rightEyeMatrix, useBufferedRendering); + ScaleProjMatrix(leftEyeMatrix, useBufferedRendering); + ScaleProjMatrix(rightEyeMatrix, useBufferedRendering); - if (!my_isnan(a) && !my_isnan(b)) { - flippedMatrix[10] = a; - flippedMatrix[14] = b; - } - } - } + render_->SetUniformM4x4Stereo("u_proj_lens", &u_proj_lens, leftEyeMatrix.m, rightEyeMatrix.m); } + Matrix4x4 flippedMatrix; + memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); + + FlipProjMatrix(flippedMatrix, useBufferedRendering); ScaleProjMatrix(flippedMatrix, useBufferedRendering); render_->SetUniformM4x4(&u_proj, flippedMatrix.m); @@ -519,18 +523,18 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu SetMatrix4x3(render_, &u_world, gstate.worldMatrix); } if (dirty & DIRTY_VIEWMATRIX) { -#ifdef OPENXR - if (flatScreen || is2D) { - SetMatrix4x3(render_, &u_view, gstate.viewMatrix); + if (IsVRBuild()) { + float leftEyeView[16]; + float rightEyeView[16]; + ConvertMatrix4x3To4x4Transposed(leftEyeView, gstate.viewMatrix); + ConvertMatrix4x3To4x4Transposed(rightEyeView, gstate.viewMatrix); + if (!flatScreen && !is2D) { + UpdateVRView(gstate.projMatrix, leftEyeView, rightEyeView); + } + render_->SetUniformM4x4Stereo("u_view", &u_view, leftEyeView, rightEyeView); } else { - float m4x4[16]; - ConvertMatrix4x3To4x4Transposed(m4x4, gstate.viewMatrix); - VR_TweakView(m4x4, gstate.projMatrix, VR_VIEW_MATRIX_LEFT_EYE); - render_->SetUniformM4x4(&u_view, m4x4); + SetMatrix4x3(render_, &u_view, gstate.viewMatrix); } -#else - SetMatrix4x3(render_, &u_view, gstate.viewMatrix); -#endif } if (dirty & DIRTY_TEXMATRIX) { SetMatrix4x3(render_, &u_texmtx, gstate.tgenMatrix); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index b91ba8e7c996..101326ee9b31 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -52,6 +52,7 @@ class LinkedShader { int u_stencilReplaceValue; int u_tex; int u_proj; + int u_proj_lens; int u_proj_through; int u_texenv; int u_view; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 259b12b00bfa..cacd51e80742 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -27,6 +27,7 @@ #include "Common/UI/View.h" #include "Common/UI/ViewGroup.h" #include "Common/UI/Context.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres #include "Common/System/System.h" @@ -1114,11 +1115,15 @@ void GameSettingsScreen::CreateViews() { LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR")); vrSettings->Add(new ItemHeader(vr->T("Virtual reality"))); vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Enable virtual reality"))); + vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), "")); CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("Enable 6 degrees of freedom movement"))); vr6DoF->SetEnabledPtr(&g_Config.bEnableVR); - PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 150, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV"))); + PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 200, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV"))); vrFieldOfView->SetEnabledPtr(&g_Config.bEnableVR); - vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), "")); + CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision (Experimental)"))); + vrStereo->SetEnabledPtr(&g_Config.bEnableVR); + PopupSliderChoice *vrStereoSepararation = vrSettings->Add(new PopupSliderChoice(&g_Config.iStereoSeparation, 1, 50, vr->T("Stereo separation (differs per game)", "Stereo separation (differs per game)"), 1, screenManager(), "x")); + vrStereoSepararation->SetEnabledPtr(&g_Config.bEnableStereo); } } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index dcd9275c06ff..871e77de184f 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -77,6 +77,7 @@ #include "Common/GraphicsContext.h" #include "Common/OSVersion.h" #include "Common/GPU/ShaderTranslation.h" +#include "Common/VR/PPSSPPVR.h" #include "Core/ControlMapper.h" #include "Core/Config.h" @@ -141,10 +142,6 @@ #include #endif -#ifdef OPENXR -#include "VR/VRRenderer.h" -#endif - ScreenManager *screenManager; std::string config_filename; @@ -1300,17 +1297,10 @@ bool NativeTouch(const TouchInput &touch) { bool NativeKey(const KeyInput &key) { - // Hack to quick enable 2D mode in VR game mode. -#ifdef OPENXR - std::vector nativeKeys; - if (KeyMap::KeyToPspButton(key.deviceId, key.keyCode, &nativeKeys)) { - for (int& nativeKey : nativeKeys) { - if (nativeKey == CTRL_SCREEN) { - VR_SetConfig(VR_CONFIG_FORCE_2D, key.flags & KEY_DOWN); - } - } + // Hack to quickly enable 2D mode in VR game mode. + if (IsVRBuild()) { + UpdateVRScreenKey(key); } -#endif // INFO_LOG(SYSTEM, "Key code: %i flags: %i", key.keyCode, key.flags); #if !defined(MOBILE_DEVICE) diff --git a/android/QuestManifest.xml b/android/QuestManifest.xml index 7501b9eabc17..8958156f220f 100644 --- a/android/QuestManifest.xml +++ b/android/QuestManifest.xml @@ -1,7 +1,7 @@ - + + MakeCurrent(); if (gl->GetMode() == GLInterfaceMode::MODE_OPENGL) - SetGLCoreContext(true); + SetGLCoreContext(!IsVRBuild()); CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); SetGPUBackend(GPUBackend::OPENGL); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index aaf9d290db83..482b864ea5dc 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -70,6 +70,7 @@ struct JNIEnv {}; #include "Common/Profiler/Profiler.h" #include "Common/Math/math_util.h" #include "Common/Data/Text/Parsers.h" +#include "Common/VR/PPSSPPVR.h" #include "Common/Log.h" #include "Common/GraphicsContext.h" @@ -93,58 +94,6 @@ struct JNIEnv {}; #include "Common/Log.h" #include "UI/GameInfoCache.h" -#ifdef OPENXR -#include "Core/HLE/sceDisplay.h" -#include "VR/VRBase.h" -#include "VR/VRInput.h" -#include "VR/VRRenderer.h" - -struct ButtonMapping { - ovrButton ovr; - int keycode; - bool pressed; - int repeat; - - ButtonMapping(int keycode, ovrButton ovr) { - this->keycode = keycode; - this->ovr = ovr; - pressed = false; - repeat = 0; - } -}; - -static std::vector leftControllerMapping = { - ButtonMapping(NKCODE_BUTTON_X, ovrButton_X), - ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y), - ButtonMapping(NKCODE_ALT_LEFT, ovrButton_GripTrigger), - ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up), - ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down), - ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left), - ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right), - ButtonMapping(NKCODE_BUTTON_THUMBL, ovrButton_LThumb), - ButtonMapping(NKCODE_ENTER, ovrButton_Trigger), - ButtonMapping(NKCODE_BACK, ovrButton_Enter), -}; - -static std::vector rightControllerMapping = { - ButtonMapping(NKCODE_BUTTON_A, ovrButton_A), - ButtonMapping(NKCODE_BUTTON_B, ovrButton_B), - ButtonMapping(NKCODE_ALT_RIGHT, ovrButton_GripTrigger), - ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up), - ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down), - ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left), - ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right), - ButtonMapping(NKCODE_BUTTON_THUMBR, ovrButton_RThumb), - ButtonMapping(NKCODE_ENTER, ovrButton_Trigger), -}; - -static const int controllerIds[] = {DEVICE_ID_XR_CONTROLLER_LEFT, DEVICE_ID_XR_CONTROLLER_RIGHT}; -static std::vector controllerMapping[2] = { - leftControllerMapping, - rightControllerMapping -}; -#endif - #include "app-android.h" bool useCPUThread = true; @@ -695,9 +644,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init renderer_inited = false; androidVersion = jAndroidVersion; deviceType = jdeviceType; -#ifdef OPENXR - deviceType = DEVICE_TYPE_VR; -#endif left_joystick_x_async = 0; left_joystick_y_async = 0; @@ -811,17 +757,10 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init EmuThreadStart(); } -#ifdef OPENXR - Version gitVer(PPSSPP_GIT_VERSION); - ovrJava java; - java.Vm = gJvm; - java.ActivityObject = nativeActivity; - java.AppVersion = gitVer.ToInteger(); - strcpy(java.AppName, "PPSSPP"); - VR_Init(java); - - __DisplaySetFramerate(72); -#endif + if (IsVRBuild()) { + Version gitVer(PPSSPP_GIT_VERSION); + InitVROnAndroid(gJvm, nativeActivity, gitVer.ToInteger(), "PPSSPP"); + } } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_audioInit(JNIEnv *, jclass) { @@ -938,13 +877,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { // JavaEGL extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { -#ifdef OPENXR - if (!renderer_inited) { - VR_EnterVR(VR_GetEngine()); - IN_VRInit(VR_GetEngine()); - } - VR_InitRenderer(VR_GetEngine()); -#endif + bool firstStart = !renderer_inited; // We should be running on the render thread here. std::string errorMessage; @@ -1006,6 +939,10 @@ extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, } NativeMessageReceived("recreateviews", ""); + if (IsVRBuild()) { + EnterVR(firstStart); + } + return true; } @@ -1044,9 +981,10 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv pixel_xres = bufw; pixel_yres = bufh; backbuffer_format = format; -#ifdef OPENXR - VR_GetResolution(VR_GetEngine(), &pixel_xres, &pixel_yres); -#endif + + if (IsVRBuild()) { + GetVRResolutionPerEye(&pixel_xres, &pixel_yres); + } recalculateDpi(); @@ -1127,33 +1065,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, UpdateRunLoopAndroid(env); } -#ifdef OPENXR - KeyInput keyInput = {}; - for (int j = 0; j < 2; j++) { - int status = IN_VRGetButtonState(j); - for (ButtonMapping& m : controllerMapping[j]) { - bool pressed = status & m.ovr; - keyInput.flags = pressed ? KEY_DOWN : KEY_UP; - keyInput.keyCode = m.keycode; - keyInput.deviceId = controllerIds[j]; - - if (m.pressed != pressed) { - if (pressed && g_Config.bHapticFeedback) { - INVR_Vibrate(100, j, 1000); - } - NativeKey(keyInput); - m.pressed = pressed; - m.repeat = 0; - } else if (pressed && (m.repeat > 30)) { - keyInput.flags |= KEY_IS_REPEAT; - NativeKey(keyInput); - m.repeat = 0; - } else { - m.repeat++; - } - } + if (IsVRBuild()) { + UpdateVRInput(NativeKey, NativeTouch, g_Config.bHapticFeedback, dp_xscale, dp_yscale); } -#endif } void System_AskForPermission(SystemPermission permission) { @@ -1372,13 +1286,14 @@ void getDesiredBackbufferSize(int &sz_x, int &sz_y) { extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JNIEnv *, jclass, jint xres, jint yres, jint dpi, jfloat refreshRate) { INFO_LOG(G3D, "NativeApp.setDisplayParameters(%d x %d, dpi=%d, refresh=%0.2f)", xres, yres, dpi, refreshRate); -#ifdef OPENXR - int width, height; - VR_GetResolution(VR_GetEngine(), &width, &height); - xres = width; - yres = height; - dpi = 320; -#endif + + if (IsVRBuild()) { + int width, height; + GetVRResolutionPerEye(&width, &height); + xres = width; + yres = height; + dpi = 320; + } bool changed = false; changed = changed || display_xres != xres || display_yres != yres; diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 9b64008b452c..758d7eb6a836 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -381,6 +381,9 @@ public void Initialize() { } int deviceType = NativeApp.DEVICE_TYPE_MOBILE; + if (isVRDevice()) { + deviceType = NativeApp.DEVICE_TYPE_VR; + } UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); switch (uiModeManager.getCurrentModeType()) { case Configuration.UI_MODE_TYPE_TELEVISION: @@ -618,7 +621,7 @@ public void onCreate(Bundle savedInstanceState) { if (javaGL) { mGLSurfaceView = new NativeGLView(this); nativeRenderer = new NativeRenderer(this); - mGLSurfaceView.setEGLContextClientVersion(2); + mGLSurfaceView.setEGLContextClientVersion(isVRDevice() ? 3 : 2); sizeManager.setSurfaceView(mGLSurfaceView); // Setup the GLSurface and ask android for the correct @@ -1542,4 +1545,8 @@ public void recreate() { finish(); } } + + private boolean isVRDevice() { + return BuildConfig.FLAVOR.compareTo("quest") == 0; + } } diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 9442f9b809a7..24a43f5b8e25 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -11,6 +11,7 @@ public class NativeApp { public static final int DEVICE_TYPE_MOBILE = 0; public static final int DEVICE_TYPE_TV = 1; public static final int DEVICE_TYPE_DESKTOP = 2; + public static final int DEVICE_TYPE_VR = 3; public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String extFilesDir, String additionalStorageDirs, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board); public static native void audioInit();