diff --git a/interface/resources/shaders/hmd_reproject.frag b/interface/resources/shaders/hmd_reproject.frag new file mode 100644 index 0000000000..adda0315a3 --- /dev/null +++ b/interface/resources/shaders/hmd_reproject.frag @@ -0,0 +1,78 @@ +// +// Created by Bradley Austin Davis on 2016/07/11 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#version 410 core + +uniform sampler2D sampler; +uniform mat3 reprojection = mat3(1); +uniform mat4 inverseProjections[2]; +uniform mat4 projections[2]; + +in vec2 vTexCoord; +in vec3 vPosition; + +out vec4 FragColor; + +void main() { + vec2 uv = vTexCoord; + + mat4 eyeInverseProjection; + mat4 eyeProjection; + + float xoffset = 1.0; + vec2 uvmin = vec2(0.0); + vec2 uvmax = vec2(1.0); + // determine the correct projection and inverse projection to use. + if (vTexCoord.x < 0.5) { + uvmax.x = 0.5; + eyeInverseProjection = inverseProjections[0]; + eyeProjection = projections[0]; + } else { + xoffset = -1.0; + uvmin.x = 0.5; + uvmax.x = 1.0; + eyeInverseProjection = inverseProjections[1]; + eyeProjection = projections[1]; + } + + // Account for stereo in calculating the per-eye NDC coordinates + vec4 ndcSpace = vec4(vPosition, 1.0); + ndcSpace.x *= 2.0; + ndcSpace.x += xoffset; + + // Convert from NDC to eyespace + vec4 eyeSpace = eyeInverseProjection * ndcSpace; + eyeSpace /= eyeSpace.w; + + // Convert to a noramlized ray + vec3 ray = eyeSpace.xyz; + ray = normalize(ray); + + // Adjust the ray by the rotation + ray = reprojection * ray; + + // Project back on to the texture plane + ray *= eyeSpace.z / ray.z; + + // Update the eyespace vector + eyeSpace.xyz = ray; + + // Reproject back into NDC + ndcSpace = eyeProjection * eyeSpace; + ndcSpace /= ndcSpace.w; + ndcSpace.x -= xoffset; + ndcSpace.x /= 2.0; + + // Calculate the new UV coordinates + uv = (ndcSpace.xy / 2.0) + 0.5; + if (any(greaterThan(uv, uvmax)) || any(lessThan(uv, uvmin))) { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } else { + FragColor = texture(sampler, uv); + } +} \ No newline at end of file diff --git a/interface/resources/shaders/hmd_reproject.vert b/interface/resources/shaders/hmd_reproject.vert new file mode 100644 index 0000000000..923375613a --- /dev/null +++ b/interface/resources/shaders/hmd_reproject.vert @@ -0,0 +1,20 @@ +// +// Created by Bradley Austin Davis on 2016/07/11 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#version 410 core +in vec3 Position; +in vec2 TexCoord; + +out vec3 vPosition; +out vec2 vTexCoord; + +void main() { + gl_Position = vec4(Position, 1); + vTexCoord = TexCoord; + vPosition = Position; +} diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index af3097ba7a..6dadcb2466 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -386,7 +386,6 @@ void OpenGLDisplayPlugin::customizeContext() { } auto renderSize = getRecommendedRenderSize(); _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); - _compositeTexture = _compositeFramebuffer->getRenderBuffer(0); } void OpenGLDisplayPlugin::uncustomizeContext() { @@ -394,7 +393,6 @@ void OpenGLDisplayPlugin::uncustomizeContext() { _cursorPipeline.reset(); _overlayPipeline.reset(); _compositeFramebuffer.reset(); - _compositeTexture.reset(); withPresentThreadLock([&] { _currentFrame.reset(); while (!_newFrameQueue.empty()) { @@ -538,7 +536,6 @@ void OpenGLDisplayPlugin::compositeLayers() { auto renderSize = getRecommendedRenderSize(); if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); - _compositeTexture = _compositeFramebuffer->getRenderBuffer(0); } { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 612dfef053..1ab7be2511 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -69,7 +69,7 @@ protected: glm::uvec2 getSurfaceSize() const; glm::uvec2 getSurfacePixels() const; - void compositeLayers(); + virtual void compositeLayers(); virtual void compositeScene(); virtual void compositeOverlay(); virtual void compositePointer(); @@ -110,7 +110,6 @@ protected: gpu::FramePointer _currentFrame; gpu::FramebufferPointer _compositeFramebuffer; - gpu::TexturePointer _compositeTexture; gpu::PipelinePointer _overlayPipeline; gpu::PipelinePointer _simplePipeline; gpu::PipelinePointer _presentPipeline; @@ -148,7 +147,7 @@ protected: } gpu::gl::GLBackend* getGLBackend(); -private: + // Any resource shared by the main thread and the presentation thread must // be serialized through this mutex mutable Mutex _presentMutex; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 9d7a497d27..2f999965f8 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -163,7 +163,7 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _compositeTexture); + batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); batch.setPipeline(_presentPipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); }); diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 44a4e879a3..5d19609f3c 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -11,6 +11,10 @@ #include +#if _DEBUG +#include +#endif + #include "Forward.h" #include "Format.h" #include "Resource.h" diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp index b30fe4705a..23e151de45 100644 --- a/libraries/gpu/src/gpu/Frame.cpp +++ b/libraries/gpu/src/gpu/Frame.cpp @@ -17,10 +17,6 @@ Frame::~Frame() { framebuffer.reset(); } - if (overlay && overlayRecycler) { - overlayRecycler(overlay); - overlay.reset(); - } assert(bufferUpdates.empty()); if (!bufferUpdates.empty()) { qFatal("Buffer sync error... frame destroyed without buffer updates being applied"); diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 1c3098f5ec..b0ecd483e0 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -32,6 +32,8 @@ namespace gpu { Mat4 pose; /// The collection of batches which make up the frame Batches batches; + /// The main thread updates to buffers that are applicable for this frame. + BufferUpdates bufferUpdates; /// The destination framebuffer in which the results will be placed FramebufferPointer framebuffer; /// The destination texture containing the 2D overlay @@ -39,10 +41,6 @@ namespace gpu { /// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE FramebufferRecycler framebufferRecycler; - /// How to process the overlay texture when the frame dies. MUST BE THREAD SAFE - OverlayRecycler overlayRecycler; - BufferUpdates bufferUpdates; - }; }; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 60b6d49d49..98516011d5 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -257,7 +257,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(_compositeTexture, false); + GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 42dcb61471..950f01496e 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -7,26 +7,23 @@ // #include "OpenVrDisplayPlugin.h" -#include - -#include -#include -#include -#include -#include +#include +#include #include -#include #include #include -#include -#include -#include #include -#include +#include #include +#include +#include +#include +#include +#include + #include "OpenVrHelpers.h" Q_DECLARE_LOGGING_CATEGORY(displayplugins) @@ -34,14 +31,197 @@ Q_DECLARE_LOGGING_CATEGORY(displayplugins) const QString OpenVrDisplayPlugin::NAME("OpenVR (Vive)"); const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probably shouldn't be hardcoded here -static vr::IVRCompositor* _compositor { nullptr }; - PoseData _nextRenderPoseData; PoseData _nextSimPoseData; -static mat4 _sensorResetMat; static std::array VR_EYES { { vr::Eye_Left, vr::Eye_Right } }; bool _openVrDisplayActive { false }; +// Flip y-axis since GL UV coords are backwards. +static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_LEFT{ 0, 0, 0.5f, 1 }; +static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_RIGHT{ 0.5f, 0, 1, 1 }; + + + +#define OPENVR_THREADED_SUBMIT 1 + + +#if OPENVR_THREADED_SUBMIT + +static QString readFile(const QString& filename) { + QFile file(filename); + file.open(QFile::Text | QFile::ReadOnly); + QString result; + result.append(QTextStream(&file).readAll()); + return result; +} + +class OpenVrSubmitThread : public QThread, public Dependency { +public: + using Mutex = std::mutex; + using Condition = std::condition_variable; + using Lock = std::unique_lock; + friend class OpenVrDisplayPlugin; + OffscreenGLCanvas _canvas; + BasicFramebufferWrapperPtr _framebuffer; + ProgramPtr _program; + ShapeWrapperPtr _plane; + struct ReprojectionUniforms { + int32_t reprojectionMatrix{ -1 }; + int32_t inverseProjectionMatrix{ -1 }; + int32_t projectionMatrix{ -1 }; + } _reprojectionUniforms; + + + OpenVrSubmitThread(OpenVrDisplayPlugin& plugin) : _plugin(plugin) { + _canvas.create(plugin._container->getPrimaryContext()); + _canvas.doneCurrent(); + _canvas.moveToThreadWithContext(this); + } + + void updateReprojectionProgram() { + static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert"; + static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag"; +#if LIVE_SHADER_RELOAD + static qint64 vsBuiltAge = 0; + static qint64 fsBuiltAge = 0; + QFileInfo vsInfo(vsFile); + QFileInfo fsInfo(fsFile); + auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); + auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); + if (!_reprojectionProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { + vsBuiltAge = vsAge; + fsBuiltAge = fsAge; +#else + if (!_program) { +#endif + QString vsSource = readFile(vsFile); + QString fsSource = readFile(fsFile); + ProgramPtr program; + try { + compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString()); + if (program) { + using namespace oglplus; + _reprojectionUniforms.reprojectionMatrix = Uniform(*program, "reprojection").Location(); + _reprojectionUniforms.inverseProjectionMatrix = Uniform(*program, "inverseProjections").Location(); + _reprojectionUniforms.projectionMatrix = Uniform(*program, "projections").Location(); + _program = program; + } + } catch (std::runtime_error& error) { + qWarning() << "Error building reprojection shader " << error.what(); + } + } + } + + void updateSource() { + Lock lock(_plugin._presentMutex); + while (!_queue.empty()) { + auto& front = _queue.front(); + auto result = glClientWaitSync(front.fence, 0, 0); + if (GL_TIMEOUT_EXPIRED == result && GL_WAIT_FAILED == result) { + break; + } + + glDeleteSync(front.fence); + front.fence = 0; + _current = front; + _queue.pop(); + } + } + + void run() override { + QThread::currentThread()->setPriority(QThread::Priority::TimeCriticalPriority); + _canvas.makeCurrent(); + glDisable(GL_DEPTH_TEST); + glViewport(0, 0, _plugin._renderTargetSize.x, _plugin._renderTargetSize.y); + _framebuffer = std::make_shared(); + _framebuffer->Init(_plugin._renderTargetSize); + updateReprojectionProgram(); + _plane = loadPlane(_program); + _canvas.doneCurrent(); + while (!_quit) { + _canvas.makeCurrent(); + updateSource(); + if (!_current.texture) { + _canvas.doneCurrent(); + QThread::usleep(1); + continue; + } + + { + auto presentRotation = glm::mat3(_nextRender.poses[0]); + auto renderRotation = glm::mat3(_current.pose); + auto correction = glm::inverse(renderRotation) * presentRotation; + _framebuffer->Bound([&] { + glBindTexture(GL_TEXTURE_2D, _current.textureID); + _program->Use(); + using namespace oglplus; + Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear); + Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear); + Uniform(*_program, _reprojectionUniforms.reprojectionMatrix).Set(correction); + //Uniform(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections); + //Uniform(*_reprojectionProgram, INVERSE_PROJECTION_MATRIX_LOCATION).Set(_eyeInverseProjections); + // FIXME what's the right oglplus mechanism to do this? It's not that ^^^ ... better yet, switch to a uniform buffer + glUniformMatrix4fv(_reprojectionUniforms.inverseProjectionMatrix, 2, GL_FALSE, &(_plugin._eyeInverseProjections[0][0][0])); + glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_plugin._eyeProjections[0][0][0])); + _plane->UseInProgram(*_program); + _plane->Draw(); + }); + static const vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; + static const vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; + + vr::Texture_t texture{ (void*)oglplus::GetName(_framebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto }; + vr::VRCompositor()->Submit(vr::Eye_Left, &texture, &leftBounds); + vr::VRCompositor()->Submit(vr::Eye_Right, &texture, &rightBounds); + PoseData nextRender, nextSim; + nextRender.frameIndex = _plugin.presentCount(); + vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses, vr::k_unMaxTrackedDeviceCount); + { + Lock lock(_plugin._presentMutex); + _presentCount++; + _presented.notify_one(); + _nextRender = nextRender; + _nextRender.update(_plugin._sensorResetMat); + _nextSim = nextSim; + _nextSim.update(_plugin._sensorResetMat); + } + } + _canvas.doneCurrent(); + } + + _canvas.makeCurrent(); + _plane.reset(); + _program.reset(); + _framebuffer.reset(); + _canvas.doneCurrent(); + + } + + void update(const CompositeInfo& newCompositeInfo) { + _queue.push(newCompositeInfo); + } + + void waitForPresent() { + auto lastCount = _presentCount.load(); + Lock lock(_plugin._presentMutex); + _presented.wait(lock, [&]()->bool { + return _presentCount.load() > lastCount; + }); + _nextSimPoseData = _nextSim; + _nextRenderPoseData = _nextRender; + } + + CompositeInfo _current; + CompositeInfo::Queue _queue; + + PoseData _nextRender, _nextSim; + bool _quit { false }; + GLuint _currentTexture { 0 }; + std::atomic _presentCount { 0 }; + Condition _presented; + OpenVrDisplayPlugin& _plugin; +}; + +#endif bool OpenVrDisplayPlugin::isSupported() const { return openVrSupported(); @@ -92,11 +272,8 @@ bool OpenVrDisplayPlugin::internalActivate() { _cullingProjection = _eyeProjections[0]; }); - _compositor = vr::VRCompositor(); - Q_ASSERT(_compositor); - // enable async time warp - // _compositor->ForceInterleavedReprojectionOn(true); + //vr::VRCompositor()->ForceInterleavedReprojectionOn(true); // set up default sensor space such that the UI overlay will align with the front of the room. auto chaperone = vr::VRChaperone(); @@ -115,11 +292,25 @@ bool OpenVrDisplayPlugin::internalActivate() { #endif } +#if OPENVR_THREADED_SUBMIT + withMainThreadContext([&] { + _submitThread = std::make_shared(*this); + }); + _submitThread->setObjectName("OpenVR Submit Thread"); + _submitThread->start(QThread::TimeCriticalPriority); +#endif + return Parent::internalActivate(); } void OpenVrDisplayPlugin::internalDeactivate() { Parent::internalDeactivate(); + +#if OPENVR_THREADED_SUBMIT + _submitThread->_quit = true; + _submitThread->wait(); +#endif + _openVrDisplayActive = false; _container->setIsOptionChecked(StandingHMDSensorMode, false); if (_system) { @@ -128,7 +319,6 @@ void OpenVrDisplayPlugin::internalDeactivate() { releaseOpenVrSystem(); _system = nullptr; } - _compositor = nullptr; } void OpenVrDisplayPlugin::customizeContext() { @@ -141,6 +331,18 @@ void OpenVrDisplayPlugin::customizeContext() { }); Parent::customizeContext(); + + _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); + for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { + if (0 != i) { + _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + } + _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false); + } +} + +void OpenVrDisplayPlugin::uncustomizeContext() { + Parent::uncustomizeContext(); } void OpenVrDisplayPlugin::resetSensors() { @@ -228,25 +430,48 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } +void OpenVrDisplayPlugin::compositeLayers() { +#if OPENVR_THREADED_SUBMIT + ++_renderingIndex; + _renderingIndex %= COMPOSITING_BUFFER_SIZE; + + auto& newComposite = _compositeInfos[_renderingIndex]; + newComposite.pose = _currentPresentFrameInfo.presentPose; + _compositeFramebuffer->setRenderBuffer(0, newComposite.texture); +#endif + + Parent::compositeLayers(); + +#if OPENVR_THREADED_SUBMIT + newComposite.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + if (!newComposite.textureID) { + newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture, false); + } + withPresentThreadLock([&] { + _submitThread->update(newComposite); + }); +#endif +} + void OpenVrDisplayPlugin::hmdPresent() { PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) - glFlush(); - // Flip y-axis since GL UV coords are backwards. - static vr::VRTextureBounds_t leftBounds { 0, 0, 0.5f, 1 }; - static vr::VRTextureBounds_t rightBounds { 0.5f, 0, 1, 1 }; - auto glTexId = getGLBackend()->getTextureID(_compositeTexture, false); - vr::Texture_t vrTexture{ (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto }; +#if OPENVR_THREADED_SUBMIT + _submitThread->waitForPresent(); - _compositor->Submit(vr::Eye_Left, &vrTexture, &leftBounds); - _compositor->Submit(vr::Eye_Right, &vrTexture, &rightBounds); - _compositor->PostPresentHandoff(); +#else + vr::Texture_t vrTexture{ (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto }; + vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); + vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); + vr::VRCompositor()->PostPresentHandoff(); +#endif } void OpenVrDisplayPlugin::postPreview() { PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) PoseData nextRender, nextSim; nextRender.frameIndex = presentCount(); +#if !OPENVR_THREADED_SUBMIT vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses, vr::k_unMaxTrackedDeviceCount); glm::mat4 resetMat; @@ -255,12 +480,12 @@ void OpenVrDisplayPlugin::postPreview() { }); nextRender.update(resetMat); nextSim.update(resetMat); - withPresentThreadLock([&] { _nextSimPoseData = nextSim; }); _nextRenderPoseData = nextRender; _hmdActivityLevel = vr::k_EDeviceActivityLevel_UserInteraction; // _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); +#endif } bool OpenVrDisplayPlugin::isHmdMounted() const { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 4c0c0aebbd..2021cf8a55 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -15,6 +15,21 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. +class OpenVrSubmitThread; + +static const size_t COMPOSITING_BUFFER_SIZE = 3; + +struct CompositeInfo { + using Queue = std::queue; + using Array = std::array; + + gpu::TexturePointer texture; + GLuint textureID { 0 }; + glm::mat4 pose; + GLsync fence{ 0 }; +}; + + class OpenVrDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: @@ -26,6 +41,7 @@ public: float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; } void customizeContext() override; + void uncustomizeContext() override; // Stereo specific methods void resetSensors() override; @@ -41,16 +57,23 @@ protected: void internalDeactivate() override; void updatePresentPose() override; + void compositeLayers() override; void hmdPresent() override; bool isHmdMounted() const override; void postPreview() override; private: + CompositeInfo::Array _compositeInfos; + size_t _renderingIndex { 0 }; vr::IVRSystem* _system { nullptr }; std::atomic _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown }; std::atomic _keyboardSupressionCount{ 0 }; static const QString NAME; vr::HmdMatrix34_t _lastGoodHMDPose; + std::shared_ptr _submitThread; + mat4 _sensorResetMat; + friend class OpenVrSubmitThread; + };