mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Restoring reprojection to OpenVR
This commit is contained in:
parent
f9d522a1ae
commit
719e555381
11 changed files with 386 additions and 46 deletions
78
interface/resources/shaders/hmd_reproject.frag
Normal file
78
interface/resources/shaders/hmd_reproject.frag
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
20
interface/resources/shaders/hmd_reproject.vert
Normal file
20
interface/resources/shaders/hmd_reproject.vert
Normal file
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#if _DEBUG
|
||||
#include <QtCore/QDebug>
|
||||
#endif
|
||||
|
||||
#include "Forward.h"
|
||||
#include "Format.h"
|
||||
#include "Resource.h"
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -7,26 +7,23 @@
|
|||
//
|
||||
#include "OpenVrDisplayPlugin.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QLoggingCategory>
|
||||
#include <QGLWidget>
|
||||
#include <QEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <gl/GlWindow.h>
|
||||
|
||||
#include <gpu/Frame.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
|
||||
#include <controllers/Pose.h>
|
||||
#include <PerfStat.h>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <PathUtils.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <controllers/Pose.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <gl/OglplusHelpers.h>
|
||||
|
||||
#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::Hmd_Eye, 2> 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<Mutex>;
|
||||
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<glm::mat3>(*program, "reprojection").Location();
|
||||
_reprojectionUniforms.inverseProjectionMatrix = Uniform<glm::mat4>(*program, "inverseProjections").Location();
|
||||
_reprojectionUniforms.projectionMatrix = Uniform<glm::mat4>(*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<BasicFramebufferWrapper>();
|
||||
_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<glm::mat3>(*_program, _reprojectionUniforms.reprojectionMatrix).Set(correction);
|
||||
//Uniform<glm::mat4>(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections);
|
||||
//Uniform<glm::mat4>(*_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<uint32_t> _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<OpenVrSubmitThread>(*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 {
|
||||
|
|
|
@ -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<CompositeInfo>;
|
||||
using Array = std::array<CompositeInfo, COMPOSITING_BUFFER_SIZE>;
|
||||
|
||||
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<vr::EDeviceActivityLevel> _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown };
|
||||
std::atomic<uint32_t> _keyboardSupressionCount{ 0 };
|
||||
static const QString NAME;
|
||||
|
||||
vr::HmdMatrix34_t _lastGoodHMDPose;
|
||||
std::shared_ptr<OpenVrSubmitThread> _submitThread;
|
||||
mat4 _sensorResetMat;
|
||||
friend class OpenVrSubmitThread;
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue