Merge pull request #5621 from highfidelity/stereo

New stereo rendering mechanism
This commit is contained in:
samcake 2015-08-21 11:23:14 -07:00
commit 66087872a3
44 changed files with 931 additions and 548 deletions

View file

@ -5,6 +5,21 @@ set(EXTERNAL_NAME LibOVR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
# These are all provided in order to allow easier testing of both
# the legacy display plugin and the new windows only plugin on
# various versions of the SDK, all on windows
#
# 0.5 public
# URL http://static.oculus.com/sdk-downloads/ovr_sdk_win_0.5.0.1.zip
# URL_MD5 d3fc4c02db9be5ff08af4ef4c97b32f9
# 0.6 public
# URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
# URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
# 0.7 alpha
# URL https://s3.amazonaws.com/static.oculus.com/sdk-downloads/0.7.0.0/Public/Alpha/ovr_sdk_win_0.7.0.0_RC1.zip
# URL_MD5 a562bb9d117087b2cf9d86653ea70fd8
if (WIN32) if (WIN32)
ExternalProject_Add( ExternalProject_Add(

View file

@ -995,6 +995,8 @@ void Application::paintGL() {
auto displayPlugin = getActiveDisplayPlugin(); auto displayPlugin = getActiveDisplayPlugin();
displayPlugin->preRender(); displayPlugin->preRender();
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
// update the avatar with a fresh HMD pose
_myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose());
auto lodManager = DependencyManager::get<LODManager>(); auto lodManager = DependencyManager::get<LODManager>();
@ -1091,7 +1093,6 @@ void Application::paintGL() {
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
} }
// Update camera position // Update camera position
if (!isHMDMode()) { if (!isHMDMode()) {
_myCamera.update(1.0f / _fps); _myCamera.update(1.0f / _fps);
@ -1100,64 +1101,46 @@ void Application::paintGL() {
// Primary rendering pass // Primary rendering pass
auto framebufferCache = DependencyManager::get<FramebufferCache>(); auto framebufferCache = DependencyManager::get<FramebufferCache>();
QSize size = framebufferCache->getFrameBufferSize(); const QSize size = framebufferCache->getFrameBufferSize();
{ {
PROFILE_RANGE(__FUNCTION__ "/mainRender"); PROFILE_RANGE(__FUNCTION__ "/mainRender");
// Viewport is assigned to the size of the framebuffer // Viewport is assigned to the size of the framebuffer
QSize size = DependencyManager::get<FramebufferCache>()->getFrameBufferSize(); renderArgs._viewport = ivec4(0, 0, size.width(), size.height());
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
{
PROFILE_RANGE(__FUNCTION__ "/clear");
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
batch.setFramebuffer(primaryFbo);
// clear the normal and specular buffers
batch.clearFramebuffer(
gpu::Framebuffer::BUFFER_COLOR0 |
gpu::Framebuffer::BUFFER_COLOR1 |
gpu::Framebuffer::BUFFER_COLOR2 |
gpu::Framebuffer::BUFFER_DEPTH,
vec4(vec3(0), 1), 1.0, 0.0);
});
}
if (displayPlugin->isStereo()) { if (displayPlugin->isStereo()) {
PROFILE_RANGE(__FUNCTION__ "/stereoRender"); // Stereo modes will typically have a larger projection matrix overall,
QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); // so we ask for the 'mono' projection matrix, which for stereo and HMD
glEnable(GL_SCISSOR_TEST); // plugins will imply the combined projection for both eyes.
for_each_eye([&](Eye eye){ //
// Load the view frustum, used by meshes // This is properly implemented for the Oculus plugins, but for OpenVR
Camera eyeCamera; // and Stereo displays I'm not sure how to get / calculate it, so we're
if (qApp->isHMDMode()) { // just relying on the left FOV in each case and hoping that the
// Allow the displayPlugin to compose the final eye transform, based on the most up-to-date head motion. // overall culling margin of error doesn't cause popping in the
eyeCamera.setTransform(displayPlugin->getModelview(eye, _myAvatar->getSensorToWorldMatrix())); // right eye. There are FIXMEs in the relevant plugins
} else { _myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection()));
eyeCamera.setTransform(displayPlugin->getModelview(eye, _myCamera.getTransform())); renderArgs._context->enableStereo(true);
} mat4 eyeViews[2];
eyeCamera.setProjection(displayPlugin->getProjection(eye, _myCamera.getProjection())); mat4 eyeProjections[2];
renderArgs._viewport = toGlm(currentViewport); auto baseProjection = renderArgs._viewFrustum->getProjection();
doInBatch(&renderArgs, [&](gpu::Batch& batch) { // FIXME we probably don't need to set the projection matrix every frame,
batch.setViewportTransform(renderArgs._viewport); // only when the display plugin changes (or in non-HMD modes when the user
batch.setStateScissorRect(renderArgs._viewport); // changes the FOV manually, which right now I don't think they can.
}); for_each_eye([&](Eye eye) {
displaySide(&renderArgs, eyeCamera); // For providing the stereo eye views, the HMD head pose has already been
}, [&] { // applied to the avatar, so we need to get the difference between the head
currentViewport.moveLeft(currentViewport.width()); // pose applied to the avatar and the per eye pose, and use THAT as
// the per-eye stereo matrix adjustment.
mat4 eyePose = displayPlugin->getEyePose(eye);
mat4 headPose = displayPlugin->getHeadPose();
mat4 eyeView = glm::inverse(eyePose) * headPose;
eyeViews[eye] = eyeView;
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
}); });
glDisable(GL_SCISSOR_TEST); renderArgs._context->setStereoProjections(eyeProjections);
} else { renderArgs._context->setStereoViews(eyeViews);
PROFILE_RANGE(__FUNCTION__ "/monoRender");
renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height());
// Viewport is assigned to the size of the framebuffer
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
batch.setViewportTransform(renderArgs._viewport);
batch.setStateScissorRect(renderArgs._viewport);
});
displaySide(&renderArgs, _myCamera);
} }
displaySide(&renderArgs, _myCamera);
doInBatch(&renderArgs, [](gpu::Batch& batch){ renderArgs._context->enableStereo(false);
doInBatch(&renderArgs, [](gpu::Batch& batch) {
batch.setFramebuffer(nullptr); batch.setFramebuffer(nullptr);
}); });
} }
@ -1191,7 +1174,6 @@ void Application::paintGL() {
PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0)); GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0));
uvec2 finalSize = toGlm(size);
// Ensure the rendering context commands are completed when rendering // Ensure the rendering context commands are completed when rendering
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// Ensure the sync object is flushed to the driver thread before releasing the context // Ensure the sync object is flushed to the driver thread before releasing the context
@ -1207,7 +1189,7 @@ void Application::paintGL() {
{ {
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
displayPlugin->display(finalTexture, finalSize); displayPlugin->display(finalTexture, toGlm(size));
} }
{ {
@ -2705,9 +2687,6 @@ void Application::update(float deltaTime) {
updateLOD(); updateLOD();
updateMouseRay(); // check what's under the mouse and update the mouse voxel updateMouseRay(); // check what's under the mouse and update the mouse voxel
// update the avatar with a fresh HMD pose
_myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose());
{ {
PerformanceTimer perfTimer("devices"); PerformanceTimer perfTimer("devices");
DeviceTracker::updateAll(); DeviceTracker::updateAll();
@ -4986,7 +4965,7 @@ mat4 Application::getEyePose(int eye) const {
mat4 Application::getEyeOffset(int eye) const { mat4 Application::getEyeOffset(int eye) const {
if (isHMDMode()) { if (isHMDMode()) {
mat4 identity; mat4 identity;
return getActiveDisplayPlugin()->getModelview((Eye)eye, identity); return getActiveDisplayPlugin()->getView((Eye)eye, identity);
} }
return mat4(); return mat4();
@ -4999,6 +4978,11 @@ mat4 Application::getHMDSensorPose() const {
return mat4(); return mat4();
} }
// FIXME there is a bug in the fullscreen setting, where leaving
// fullscreen does not restore the window frame, making it difficult
// or impossible to move or size the window.
// Additionally, setting fullscreen isn't hiding the menu on windows
// make it useless for stereoscopic modes.
void Application::setFullscreen(const QScreen* target) { void Application::setFullscreen(const QScreen* target) {
if (!_window->isFullScreen()) { if (!_window->isFullScreen()) {
_savedGeometry = _window->geometry(); _savedGeometry = _window->geometry();

View file

@ -191,7 +191,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
auto textureCache = DependencyManager::get<TextureCache>(); auto textureCache = DependencyManager::get<TextureCache>();
gpu::Batch batch; gpu::Batch& batch = *renderArgs->_batch;
batch.setViewTransform(Transform()); batch.setViewTransform(Transform());
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection()); batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) * batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
@ -219,6 +219,4 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
batch.setInputBuffer(VERTICES_SLOT, posView); batch.setInputBuffer(VERTICES_SLOT, posView);
batch.setInputBuffer(COLOR_SLOT, colView); batch.setInputBuffer(COLOR_SLOT, colView);
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
renderArgs->_context->render(batch);
} }

View file

@ -211,7 +211,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
//Handle fading and deactivation/activation of UI //Handle fading and deactivation/activation of UI
gpu::Batch batch; gpu::Batch batch;
renderArgs->_context->syncCache();
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->useSimpleDrawPipeline(batch); geometryCache->useSimpleDrawPipeline(batch);
@ -279,7 +278,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
vec2 canvasSize = qApp->getCanvasSize(); vec2 canvasSize = qApp->getCanvasSize();
_textureAspectRatio = aspect(canvasSize); _textureAspectRatio = aspect(canvasSize);
renderArgs->_context->syncCache();
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
gpu::Batch batch; gpu::Batch batch;

View file

@ -92,7 +92,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
renderStatsAndLogs(renderArgs); // currently renders nothing renderStatsAndLogs(renderArgs); // currently renders nothing
renderArgs->_context->syncCache();
renderArgs->_context->render(batch); renderArgs->_context->render(batch);
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch

View file

@ -13,8 +13,8 @@
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display");
const QString MENU_PATH = "Display"; static const QString MENU_PATH = "Display";
const QString FULLSCREEN = "Fullscreen"; static const QString FULLSCREEN = "Fullscreen";
const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
return NAME; return NAME;
@ -30,11 +30,11 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
CONTAINER->unsetFullscreen(); CONTAINER->unsetFullscreen();
} }
}, true, false); }, true, false);
MainWindowOpenGLDisplayPlugin::activate(); WindowOpenGLDisplayPlugin::activate();
} }
void Basic2DWindowOpenGLDisplayPlugin::deactivate() { void Basic2DWindowOpenGLDisplayPlugin::deactivate() {
MainWindowOpenGLDisplayPlugin::deactivate(); WindowOpenGLDisplayPlugin::deactivate();
} }
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const { int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const {

View file

@ -7,10 +7,10 @@
// //
#pragma once #pragma once
#include "MainWindowOpenGLDisplayPlugin.h" #include "WindowOpenGLDisplayPlugin.h"
class QScreen; class QScreen;
class Basic2DWindowOpenGLDisplayPlugin : public MainWindowOpenGLDisplayPlugin { class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
Q_OBJECT Q_OBJECT
public: public:

View file

@ -15,8 +15,8 @@
#include "Basic2DWindowOpenGLDisplayPlugin.h" #include "Basic2DWindowOpenGLDisplayPlugin.h"
#include "openvr/OpenVrDisplayPlugin.h" #include "openvr/OpenVrDisplayPlugin.h"
#include "oculus/Oculus_0_5_DisplayPlugin.h" #include "oculus/OculusDisplayPlugin.h"
#include "oculus/Oculus_0_6_DisplayPlugin.h" #include "oculus/OculusLegacyDisplayPlugin.h"
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
DisplayPluginList getDisplayPlugins() { DisplayPluginList getDisplayPlugins() {
@ -28,13 +28,17 @@ DisplayPluginList getDisplayPlugins() {
// Stereo modes // Stereo modes
// FIXME fix stereo display plugins // FIXME fix stereo display plugins
//new SideBySideStereoDisplayPlugin(), new SideBySideStereoDisplayPlugin(),
//new InterleavedStereoDisplayPlugin(), //new InterleavedStereoDisplayPlugin(),
// HMDs // HMDs
new Oculus_0_5_DisplayPlugin(),
new Oculus_0_6_DisplayPlugin(), // Windows Oculus SDK
new OculusDisplayPlugin(),
// Mac/Linux Oculus SDK (0.5)
new OculusLegacyDisplayPlugin(),
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// SteamVR SDK
new OpenVrDisplayPlugin(), new OpenVrDisplayPlugin(),
#endif #endif
nullptr nullptr

View file

@ -97,12 +97,12 @@ public:
return baseProjection; return baseProjection;
} }
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const { virtual glm::mat4 getView(Eye eye, const glm::mat4& baseView) const {
return glm::inverse(getEyePose(eye)) * baseModelview; return glm::inverse(getEyePose(eye)) * baseView;
} }
// HMD specific methods // HMD specific methods
// TODO move these into another class // TODO move these into another class?
virtual glm::mat4 getEyePose(Eye eye) const { virtual glm::mat4 getEyePose(Eye eye) const {
static const glm::mat4 pose; return pose; static const glm::mat4 pose; return pose;
} }
@ -115,11 +115,6 @@ public:
virtual void resetSensors() {} virtual void resetSensors() {}
virtual float devicePixelRatio() { return 1.0; } virtual float devicePixelRatio() { return 1.0; }
//// The window for the surface, used for event interception. May be null.
//virtual QWindow* getWindow() const = 0;
//virtual void installEventFilter(QObject* filter) {}
//virtual void removeEventFilter(QObject* filter) {}
signals: signals:
void recommendedFramebufferSizeChanged(const QSize & size); void recommendedFramebufferSizeChanged(const QSize & size);

View file

@ -1,9 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#include "MainWindowOpenGLDisplayPlugin.h"

View file

@ -1,13 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "WindowOpenGLDisplayPlugin.h"
class MainWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
};

View file

@ -1,76 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#include "OculusBaseDisplayPlugin.h"
#include <ViewFrustum.h>
#include "OculusHelpers.h"
using namespace Oculus;
void OculusBaseDisplayPlugin::activate() {
glm::uvec2 eyeSizes[2];
ovr_for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmd->MaxEyeFov[eye];
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
});
_desiredFramebufferSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
MainWindowOpenGLDisplayPlugin::activate();
}
uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const {
return _desiredFramebufferSize;
}
void OculusBaseDisplayPlugin::preRender() {
ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, nullptr);
}
glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
return _eyeProjections[eye];
}
glm::mat4 OculusBaseDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelview) const {
return baseModelview * toGlm(_eyePoses[eye]);
}
void OculusBaseDisplayPlugin::resetSensors() {
ovrHmd_RecenterPose(_hmd);
}
glm::mat4 OculusBaseDisplayPlugin::getEyePose(Eye eye) const {
return toGlm(_eyePoses[eye]);
}
// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for
// any use of head poses for rendering, ensuring you use the correct eye
glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const {
ovrTrackingState state = ovrHmd_GetTrackingState(_hmd, 0.0f);
return toGlm(state.HeadPose.ThePose);
}

View file

@ -1,26 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "../MainWindowOpenGLDisplayPlugin.h"
class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
public:
// Stereo specific methods
virtual bool isHmd() const override { return true; }
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override;
virtual void activate() override;
virtual void preRender() override;
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
virtual void resetSensors() override;
virtual glm::mat4 getEyePose(Eye eye) const override;
virtual glm::mat4 getHeadPose() const override;
};

View file

@ -5,7 +5,7 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "Oculus_0_6_DisplayPlugin.h" #include "OculusDisplayPlugin.h"
#include <memory> #include <memory>
@ -15,9 +15,7 @@
#include <GlWindow.h> #include <GlWindow.h>
#include <QEvent> #include <QEvent>
#include <QResizeEvent> #include <QResizeEvent>
#include <QThread>
#include <OVR_CAPI_GL.h>
#include <OglplusHelpers.h> #include <OglplusHelpers.h>
#include <oglplus/opt/list_init.hpp> #include <oglplus/opt/list_init.hpp>
@ -27,18 +25,34 @@
#include <PerfStat.h> #include <PerfStat.h>
#include <plugins/PluginContainer.h> #include <plugins/PluginContainer.h>
#include <ViewFrustum.h>
#include "OculusHelpers.h" #include "OculusHelpers.h"
using namespace Oculus; #if (OVR_MAJOR_VERSION == 6)
#if (OVR_MAJOR_VERSION == 6) #define ovr_Create ovrHmd_Create
SwapFboPtr _sceneFbo; #define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL
MirrorFboPtr _mirrorFbo; #define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL
ovrLayerEyeFov _sceneLayer; #define ovr_Destroy ovrHmd_Destroy
#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet
#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture
#define ovr_GetFloat ovrHmd_GetFloat
#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize
#define ovr_GetFrameTiming ovrHmd_GetFrameTiming
#define ovr_GetTrackingState ovrHmd_GetTrackingState
#define ovr_GetRenderDesc ovrHmd_GetRenderDesc
#define ovr_RecenterPose ovrHmd_RecenterPose
#define ovr_SubmitFrame ovrHmd_SubmitFrame
#define ovr_ConfigureTracking ovrHmd_ConfigureTracking
#define ovr_GetHmdDesc(X) *X
#endif
#if (OVR_MAJOR_VERSION >= 6)
// A base class for FBO wrappers that need to use the Oculus C // A base class for FBO wrappers that need to use the Oculus C
// API to manage textures via ovrHmd_CreateSwapTextureSetGL, // API to manage textures via ovr_CreateSwapTextureSetGL,
// ovrHmd_CreateMirrorTextureGL, etc // ovr_CreateMirrorTextureGL, etc
template <typename C> template <typename C>
struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> { struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> {
ovrHmd hmd; ovrHmd hmd;
@ -73,7 +87,7 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*
~SwapFramebufferWrapper() { ~SwapFramebufferWrapper() {
if (color) { if (color) {
ovrHmd_DestroySwapTextureSet(hmd, color); ovr_DestroySwapTextureSet(hmd, color);
color = nullptr; color = nullptr;
} }
} }
@ -86,11 +100,11 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*
protected: protected:
virtual void initColor() override { virtual void initColor() override {
if (color) { if (color) {
ovrHmd_DestroySwapTextureSet(hmd, color); ovr_DestroySwapTextureSet(hmd, color);
color = nullptr; color = nullptr;
} }
if (!OVR_SUCCESS(ovrHmd_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color))) { if (!OVR_SUCCESS(ovr_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color))) {
qFatal("Unable to create swap textures"); qFatal("Unable to create swap textures");
} }
@ -127,7 +141,7 @@ struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {
virtual ~MirrorFramebufferWrapper() { virtual ~MirrorFramebufferWrapper() {
if (color) { if (color) {
ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color);
color = nullptr; color = nullptr;
} }
} }
@ -135,10 +149,10 @@ struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {
private: private:
void initColor() override { void initColor() override {
if (color) { if (color) {
ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color);
color = nullptr; color = nullptr;
} }
ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color); ovrResult result = ovr_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color);
Q_ASSERT(OVR_SUCCESS(result)); Q_ASSERT(OVR_SUCCESS(result));
} }
@ -149,17 +163,47 @@ private:
} }
}; };
#endif #endif
const QString OculusDisplayPlugin::NAME("Oculus Rift");
const QString Oculus_0_6_DisplayPlugin::NAME("Oculus Rift"); uvec2 OculusDisplayPlugin::getRecommendedRenderSize() const {
return _desiredFramebufferSize;
}
const QString & Oculus_0_6_DisplayPlugin::getName() const { void OculusDisplayPlugin::preRender() {
#if (OVR_MAJOR_VERSION >= 6)
ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex);
_trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds);
ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses);
#endif
}
glm::mat4 OculusDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
return _eyeProjections[eye];
}
void OculusDisplayPlugin::resetSensors() {
#if (OVR_MAJOR_VERSION >= 6)
ovr_RecenterPose(_hmd);
#endif
}
glm::mat4 OculusDisplayPlugin::getEyePose(Eye eye) const {
return toGlm(_eyePoses[eye]);
}
glm::mat4 OculusDisplayPlugin::getHeadPose() const {
return toGlm(_trackingState.HeadPose.ThePose);
}
const QString & OculusDisplayPlugin::getName() const {
return NAME; return NAME;
} }
bool Oculus_0_6_DisplayPlugin::isSupported() const { bool OculusDisplayPlugin::isSupported() const {
#if (OVR_MAJOR_VERSION == 6) #if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
return false; return false;
} }
@ -174,27 +218,81 @@ bool Oculus_0_6_DisplayPlugin::isSupported() const {
#endif #endif
} }
void OculusDisplayPlugin::init() {
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
qFatal("Could not init OVR");
}
}
#if (OVR_MAJOR_VERSION == 6) void OculusDisplayPlugin::deinit() {
ovrLayerEyeFov& getSceneLayer() { ovr_Shutdown();
}
#if (OVR_MAJOR_VERSION >= 6)
ovrLayerEyeFov& OculusDisplayPlugin::getSceneLayer() {
return _sceneLayer; return _sceneLayer;
} }
#endif #endif
//static gpu::TexturePointer _texture; //static gpu::TexturePointer _texture;
void Oculus_0_6_DisplayPlugin::activate() { void OculusDisplayPlugin::activate() {
#if (OVR_MAJOR_VERSION == 6) #if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
Q_ASSERT(false); Q_ASSERT(false);
qFatal("Failed to Initialize SDK"); qFatal("Failed to Initialize SDK");
} }
if (!OVR_SUCCESS(ovrHmd_Create(0, &_hmd))) {
// CONTAINER->getPrimarySurface()->makeCurrent();
#if (OVR_MAJOR_VERSION == 6)
if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) {
#elif (OVR_MAJOR_VERSION == 7)
if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) {
#endif
Q_ASSERT(false); Q_ASSERT(false);
qFatal("Failed to acquire HMD"); qFatal("Failed to acquire HMD");
} }
OculusBaseDisplayPlugin::activate(); _hmdDesc = ovr_GetHmdDesc(_hmd);
_ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd);
glm::uvec2 eyeSizes[2];
ovr_for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
});
ovrFovPort combined = _eyeFovs[Left];
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
_desiredFramebufferSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
WindowOpenGLDisplayPlugin::activate();
// Parent class relies on our _hmd intialization, so it must come after that. // Parent class relies on our _hmd intialization, so it must come after that.
ovrLayerEyeFov& sceneLayer = getSceneLayer(); ovrLayerEyeFov& sceneLayer = getSceneLayer();
@ -203,7 +301,7 @@ void Oculus_0_6_DisplayPlugin::activate() {
sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
ovr_for_each_eye([&](ovrEyeType eye) { ovr_for_each_eye([&](ovrEyeType eye) {
ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov; ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
ovrSizei & size = sceneLayer.Viewport[eye].Size = ovrHmd_GetFovTextureSize(_hmd, eye, fov, 1.0f); ovrSizei & size = sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f);
sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
}); });
// We're rendering both eyes to the same texture, so only one of the // We're rendering both eyes to the same texture, so only one of the
@ -214,17 +312,16 @@ void Oculus_0_6_DisplayPlugin::activate() {
PerformanceTimer::setActive(true); PerformanceTimer::setActive(true);
if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd, if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device"); qFatal("Could not attach to sensor device");
} }
#endif #endif
} }
void Oculus_0_6_DisplayPlugin::customizeContext() { void OculusDisplayPlugin::customizeContext() {
#if (OVR_MAJOR_VERSION == 6) WindowOpenGLDisplayPlugin::customizeContext();
OculusBaseDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6)
//_texture = DependencyManager::get<TextureCache>()-> //_texture = DependencyManager::get<TextureCache>()->
// getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png"); // getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png");
uvec2 mirrorSize = toGlm(_window->geometry().size()); uvec2 mirrorSize = toGlm(_window->geometry().size());
@ -236,24 +333,29 @@ void Oculus_0_6_DisplayPlugin::customizeContext() {
#endif #endif
} }
void Oculus_0_6_DisplayPlugin::deactivate() { void OculusDisplayPlugin::deactivate() {
#if (OVR_MAJOR_VERSION == 6) #if (OVR_MAJOR_VERSION >= 6)
makeCurrent(); makeCurrent();
_sceneFbo.reset(); _sceneFbo.reset();
_mirrorFbo.reset(); _mirrorFbo.reset();
doneCurrent(); doneCurrent();
PerformanceTimer::setActive(false); PerformanceTimer::setActive(false);
OculusBaseDisplayPlugin::deactivate(); WindowOpenGLDisplayPlugin::deactivate();
ovrHmd_Destroy(_hmd); ovr_Destroy(_hmd);
_hmd = nullptr; _hmd = nullptr;
ovr_Shutdown(); ovr_Shutdown();
#endif #endif
} }
void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
#if (OVR_MAJOR_VERSION == 6) static bool inDisplay = false;
if (inDisplay) {
return;
}
inDisplay = true;
#if (OVR_MAJOR_VERSION >= 6)
using namespace oglplus; using namespace oglplus;
// Need to make sure only the display plugin is responsible for // Need to make sure only the display plugin is responsible for
// controlling vsync // controlling vsync
@ -263,6 +365,7 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
auto size = _sceneFbo->size; auto size = _sceneFbo->size;
Context::Viewport(size.x, size.y); Context::Viewport(size.x, size.y);
glBindTexture(GL_TEXTURE_2D, finalTexture); glBindTexture(GL_TEXTURE_2D, finalTexture);
GLenum err = glGetError();
drawUnitQuad(); drawUnitQuad();
}); });
@ -280,17 +383,25 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
the UI visible in the output window (unlikely). This should be done before the UI visible in the output window (unlikely). This should be done before
_sceneFbo->Increment or we're be using the wrong texture _sceneFbo->Increment or we're be using the wrong texture
*/ */
//_sceneFbo->Bound(GL_READ_FRAMEBUFFER, [&] { _sceneFbo->Bound(Framebuffer::Target::Read, [&] {
// glBlitFramebuffer( glBlitFramebuffer(
// 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, 0, 0, _sceneFbo->size.x, _sceneFbo->size.y,
// 0, 0, windowSize.x, _mirrorFbo.y, 0, 0, windowSize.x, windowSize.y,
// GL_COLOR_BUFFER_BIT, GL_NEAREST); GL_COLOR_BUFFER_BIT, GL_NEAREST);
//}); });
{ {
PerformanceTimer("OculusSubmit"); PerformanceTimer("OculusSubmit");
ovrViewScaleDesc viewScaleDesc;
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1];
ovrLayerHeader* layers = &sceneLayer.Header; ovrLayerHeader* layers = &sceneLayer.Header;
ovrResult result = ovrHmd_SubmitFrame(_hmd, _frameIndex, nullptr, &layers, 1); ovrResult result = ovr_SubmitFrame(_hmd, 0, &viewScaleDesc, &layers, 1);
if (!OVR_SUCCESS(result)) {
qDebug() << result;
}
} }
_sceneFbo->Increment(); _sceneFbo->Increment();
@ -299,21 +410,22 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
will contain the post-distorted and fully composited scene regardless of how many layers will contain the post-distorted and fully composited scene regardless of how many layers
we send. we send.
*/ */
auto mirrorSize = _mirrorFbo->size; //auto mirrorSize = _mirrorFbo->size;
_mirrorFbo->Bound(Framebuffer::Target::Read, [&] { //_mirrorFbo->Bound(Framebuffer::Target::Read, [&] {
Context::BlitFramebuffer( // Context::BlitFramebuffer(
0, mirrorSize.y, mirrorSize.x, 0, // 0, mirrorSize.y, mirrorSize.x, 0,
0, 0, windowSize.x, windowSize.y, // 0, 0, windowSize.x, windowSize.y,
BufferSelectBit::ColorBuffer, BlitFilter::Nearest); // BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
}); //});
++_frameIndex; ++_frameIndex;
#endif #endif
inDisplay = false;
} }
// Pass input events on to the application // Pass input events on to the application
bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
#if (OVR_MAJOR_VERSION == 6) #if (OVR_MAJOR_VERSION >= 6)
if (event->type() == QEvent::Resize) { if (event->type() == QEvent::Resize) {
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event); QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height(); qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height();
@ -323,7 +435,7 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
doneCurrent(); doneCurrent();
} }
#endif #endif
return OculusBaseDisplayPlugin::eventFilter(receiver, event); return WindowOpenGLDisplayPlugin::eventFilter(receiver, event);
} }
/* /*
@ -331,8 +443,8 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
However, it should only be done if we can reliably disable v-sync on the mirror surface, However, it should only be done if we can reliably disable v-sync on the mirror surface,
otherwise the swapbuffer delay will interefere with the framerate of the headset otherwise the swapbuffer delay will interefere with the framerate of the headset
*/ */
void Oculus_0_6_DisplayPlugin::finishFrame() { void OculusDisplayPlugin::finishFrame() {
swapBuffers(); //swapBuffers();
doneCurrent(); doneCurrent();
}; };

View file

@ -0,0 +1,78 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "../WindowOpenGLDisplayPlugin.h"
#include <QTimer>
#include <OVR_CAPI.h>
class OffscreenGlCanvas;
struct SwapFramebufferWrapper;
struct MirrorFramebufferWrapper;
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
using MirrorFboPtr = QSharedPointer<MirrorFramebufferWrapper>;
class OculusDisplayPlugin : public WindowOpenGLDisplayPlugin {
public:
virtual bool isSupported() const override;
virtual const QString & getName() const override;
virtual void init() override;
virtual void deinit() override;
virtual void activate() override;
virtual void deactivate() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
// Stereo specific methods
virtual bool isHmd() const override { return true; }
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
virtual void resetSensors() override;
virtual glm::mat4 getEyePose(Eye eye) const override;
virtual glm::mat4 getHeadPose() const override;
protected:
virtual void preRender() override;
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
static const QString NAME;
ovrHmd _hmd;
float _ipd{ OVR_DEFAULT_IPD };
unsigned int _frameIndex;
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrPosef _eyePoses[2];
ovrVector3f _eyeOffsets[2];
ovrFovPort _eyeFovs[2];
mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize;
ovrTrackingState _trackingState;
#if (OVR_MAJOR_VERSION >= 6)
ovrLayerEyeFov& getSceneLayer();
ovrHmdDesc _hmdDesc;
SwapFboPtr _sceneFbo;
MirrorFboPtr _mirrorFbo;
ovrLayerEyeFov _sceneLayer;
#endif
#if (OVR_MAJOR_VERSION == 7)
ovrGraphicsLuid _luid;
#endif
};

View file

@ -7,7 +7,7 @@
// //
#pragma once #pragma once
#include <OVR_CAPI.h> #include <OVR_CAPI_GL.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
@ -79,14 +79,3 @@ inline ovrQuatf ovrFromGlm(const glm::quat & q) {
return{ q.x, q.y, q.z, q.w }; return{ q.x, q.y, q.z, q.w };
} }
namespace Oculus {
extern ovrHmd _hmd;
extern unsigned int _frameIndex;
extern ovrEyeRenderDesc _eyeRenderDescs[2];
extern ovrPosef _eyePoses[2];
extern ovrVector3f _eyeOffsets[2];
extern ovrFovPort _eyeFovs[2];
extern mat4 _eyeProjections[2];
extern mat4 _compositeEyeProjections[2];
extern uvec2 _desiredFramebufferSize;
}

View file

@ -5,7 +5,7 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "Oculus_0_5_DisplayPlugin.h" #include "OculusLegacyDisplayPlugin.h"
#include <memory> #include <memory>
@ -19,34 +19,66 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QScreen> #include <QScreen>
#include <OVR_CAPI_GL.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <OglplusHelpers.h> #include <OglplusHelpers.h>
#include <ViewFrustum.h>
#include "plugins/PluginContainer.h" #include "plugins/PluginContainer.h"
#include "OculusHelpers.h" #include "OculusHelpers.h"
using namespace Oculus;
ovrTexture _eyeTextures[2];
int _hmdScreen{ -1 };
bool _hswDismissed{ false };
DisplayPlugin* makeOculusDisplayPlugin() {
return new Oculus_0_5_DisplayPlugin();
}
using namespace oglplus; using namespace oglplus;
const QString Oculus_0_5_DisplayPlugin::NAME("Oculus Rift (0.5)"); const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5)");
const QString & Oculus_0_5_DisplayPlugin::getName() const { const QString & OculusLegacyDisplayPlugin::getName() const {
return NAME; return NAME;
} }
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) {
}
bool Oculus_0_5_DisplayPlugin::isSupported() const { uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const {
return _desiredFramebufferSize;
}
void OculusLegacyDisplayPlugin::preRender() {
#if (OVR_MAJOR_VERSION == 5)
ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, &_trackingState);
ovrHmd_BeginFrame(_hmd, _frameIndex);
#endif
WindowOpenGLDisplayPlugin::preRender();
}
glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
return _eyeProjections[eye];
}
void OculusLegacyDisplayPlugin::resetSensors() {
#if (OVR_MAJOR_VERSION == 5)
ovrHmd_RecenterPose(_hmd);
#endif
}
glm::mat4 OculusLegacyDisplayPlugin::getEyePose(Eye eye) const {
#if (OVR_MAJOR_VERSION == 5)
return toGlm(_eyePoses[eye]);
#else
return WindowOpenGLDisplayPlugin::getEyePose(eye);
#endif
}
// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for
// any use of head poses for rendering, ensuring you use the correct eye
glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const {
#if (OVR_MAJOR_VERSION == 5)
return toGlm(_trackingState.HeadPose.ThePose);
#else
return WindowOpenGLDisplayPlugin::getHeadPose();
#endif
}
bool OculusLegacyDisplayPlugin::isSupported() const {
#if (OVR_MAJOR_VERSION == 5) #if (OVR_MAJOR_VERSION == 5)
if (!ovr_Initialize(nullptr)) { if (!ovr_Initialize(nullptr)) {
return false; return false;
@ -77,7 +109,7 @@ bool Oculus_0_5_DisplayPlugin::isSupported() const {
#endif #endif
} }
void Oculus_0_5_DisplayPlugin::activate() { void OculusLegacyDisplayPlugin::activate() {
#if (OVR_MAJOR_VERSION == 5) #if (OVR_MAJOR_VERSION == 5)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
Q_ASSERT(false); Q_ASSERT(false);
@ -89,7 +121,41 @@ void Oculus_0_5_DisplayPlugin::activate() {
qFatal("Failed to acquire HMD"); qFatal("Failed to acquire HMD");
} }
OculusBaseDisplayPlugin::activate(); glm::uvec2 eyeSizes[2];
ovr_for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmd->MaxEyeFov[eye];
ovrEyeRenderDesc erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
});
ovrFovPort combined = _eyeFovs[Left];
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
_desiredFramebufferSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
WindowOpenGLDisplayPlugin::activate();
int screen = getHmdScreen(); int screen = getHmdScreen();
if (screen != -1) { if (screen != -1) {
CONTAINER->setFullscreen(qApp->screens()[screen]); CONTAINER->setFullscreen(qApp->screens()[screen]);
@ -118,17 +184,16 @@ void Oculus_0_5_DisplayPlugin::activate() {
} }
}); });
ovrEyeRenderDesc _eyeRenderDescs[ovrEye_Count];
ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs);
Q_ASSERT(result); Q_ASSERT(result);
#endif #endif
} }
void Oculus_0_5_DisplayPlugin::deactivate() { void OculusLegacyDisplayPlugin::deactivate() {
#if (OVR_MAJOR_VERSION == 5) #if (OVR_MAJOR_VERSION == 5)
_window->removeEventFilter(this); _window->removeEventFilter(this);
OculusBaseDisplayPlugin::deactivate(); WindowOpenGLDisplayPlugin::deactivate();
QScreen* riftScreen = nullptr; QScreen* riftScreen = nullptr;
if (_hmdScreen >= 0) { if (_hmdScreen >= 0) {
@ -142,18 +207,11 @@ void Oculus_0_5_DisplayPlugin::deactivate() {
#endif #endif
} }
void Oculus_0_5_DisplayPlugin::preRender() { void OculusLegacyDisplayPlugin::preDisplay() {
#if (OVR_MAJOR_VERSION == 5)
OculusBaseDisplayPlugin::preRender();
ovrHmd_BeginFrame(_hmd, _frameIndex);
#endif
}
void Oculus_0_5_DisplayPlugin::preDisplay() {
_window->makeCurrent(); _window->makeCurrent();
} }
void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
++_frameIndex; ++_frameIndex;
#if (OVR_MAJOR_VERSION == 5) #if (OVR_MAJOR_VERSION == 5)
ovr_for_each_eye([&](ovrEyeType eye) { ovr_for_each_eye([&](ovrEyeType eye) {
@ -164,7 +222,7 @@ void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
} }
// Pass input events on to the application // Pass input events on to the application
bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
#if (OVR_MAJOR_VERSION == 5) #if (OVR_MAJOR_VERSION == 5)
if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { if (!_hswDismissed && (event->type() == QEvent::KeyPress)) {
static ovrHSWDisplayState hswState; static ovrHSWDisplayState hswState;
@ -176,17 +234,19 @@ bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
} }
} }
#endif #endif
return OculusBaseDisplayPlugin::eventFilter(receiver, event); return WindowOpenGLDisplayPlugin::eventFilter(receiver, event);
} }
// FIXME mirroring tot he main window is diffucult on OSX because it requires that we // FIXME mirroring tot he main window is diffucult on OSX because it requires that we
// trigger a swap, which causes the client to wait for the v-sync of the main screen running // trigger a swap, which causes the client to wait for the v-sync of the main screen running
// at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate // at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate
// thread // thread
void Oculus_0_5_DisplayPlugin::finishFrame() { // FIXME If we move to the 'batch rendering on a different thread' we can possibly do this.
// however, we need to make sure it doesn't block the event handling.
void OculusLegacyDisplayPlugin::finishFrame() {
_window->doneCurrent(); _window->doneCurrent();
}; };
int Oculus_0_5_DisplayPlugin::getHmdScreen() const { int OculusLegacyDisplayPlugin::getHmdScreen() const {
return _hmdScreen; return _hmdScreen;
} }

View file

@ -0,0 +1,63 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "../WindowOpenGLDisplayPlugin.h"
#include <QTimer>
#include <OVR_CAPI.h>
class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin {
public:
OculusLegacyDisplayPlugin();
virtual bool isSupported() const override;
virtual const QString & getName() const override;
virtual void activate() override;
virtual void deactivate() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
virtual int getHmdScreen() const override;
// Stereo specific methods
virtual bool isHmd() const override { return true; }
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
virtual void resetSensors() override;
virtual glm::mat4 getEyePose(Eye eye) const override;
virtual glm::mat4 getHeadPose() const override;
protected:
virtual void preRender() override;
virtual void preDisplay() override;
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
static const QString NAME;
float _ipd{ OVR_DEFAULT_IPD };
ovrHmd _hmd;
unsigned int _frameIndex;
ovrTrackingState _trackingState;
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrPosef _eyePoses[2];
ovrVector3f _eyeOffsets[2];
ovrFovPort _eyeFovs[2];
mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize;
ovrTexture _eyeTextures[2];
mutable int _hmdScreen{ -1 };
bool _hswDismissed{ false };
};

View file

@ -1,37 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "OculusBaseDisplayPlugin.h"
#include <QTimer>
class Oculus_0_5_DisplayPlugin : public OculusBaseDisplayPlugin {
public:
virtual bool isSupported() const override;
virtual const QString & getName() const override;
virtual void activate() override;
virtual void deactivate() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
virtual int getHmdScreen() const override;
protected:
virtual void preRender() override;
virtual void preDisplay() override;
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
static const QString NAME;
};

View file

@ -1,41 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "OculusBaseDisplayPlugin.h"
#include <QTimer>
class OffscreenGlCanvas;
struct SwapFramebufferWrapper;
struct MirrorFramebufferWrapper;
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
using MirrorFboPtr = QSharedPointer<MirrorFramebufferWrapper>;
class Oculus_0_6_DisplayPlugin : public OculusBaseDisplayPlugin {
public:
virtual bool isSupported() const override;
virtual const QString & getName() const override;
virtual void activate() override;
virtual void deactivate() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
static const QString NAME;
};

View file

@ -128,7 +128,7 @@ void OpenVrDisplayPlugin::activate() {
delete[] buffer; delete[] buffer;
} }
Q_ASSERT(unSize <= 1); Q_ASSERT(unSize <= 1);
MainWindowOpenGLDisplayPlugin::activate(); WindowOpenGLDisplayPlugin::activate();
} }
void OpenVrDisplayPlugin::deactivate() { void OpenVrDisplayPlugin::deactivate() {
@ -141,7 +141,7 @@ void OpenVrDisplayPlugin::deactivate() {
_hmd = nullptr; _hmd = nullptr;
} }
_compositor = nullptr; _compositor = nullptr;
MainWindowOpenGLDisplayPlugin::deactivate(); WindowOpenGLDisplayPlugin::deactivate();
} }
uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const { uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
@ -149,19 +149,19 @@ uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
} }
mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) const { mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) const {
// FIXME hack to ensure that we don't crash trying to get the combined matrix
if (eye == Mono) {
eye = Left;
}
return _eyesData[eye]._projectionMatrix; return _eyesData[eye]._projectionMatrix;
} }
glm::mat4 OpenVrDisplayPlugin::getModelview(Eye eye, const mat4& baseModelview) const {
return baseModelview * getEyePose(eye);
}
void OpenVrDisplayPlugin::resetSensors() { void OpenVrDisplayPlugin::resetSensors() {
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0])); _sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0]));
} }
glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const { glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const {
return getHeadPose() * _eyesData[eye]._eyeOffset; return _eyesData[eye]._eyeOffset * getHeadPose();
} }
glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { glm::mat4 OpenVrDisplayPlugin::getHeadPose() const {
@ -169,7 +169,7 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose() const {
} }
void OpenVrDisplayPlugin::customizeContext() { void OpenVrDisplayPlugin::customizeContext() {
MainWindowOpenGLDisplayPlugin::customizeContext(); WindowOpenGLDisplayPlugin::customizeContext();
} }
void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {

View file

@ -11,9 +11,9 @@
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#include "../MainWindowOpenGLDisplayPlugin.h" #include "../WindowOpenGLDisplayPlugin.h"
class OpenVrDisplayPlugin : public MainWindowOpenGLDisplayPlugin { class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin {
public: public:
virtual bool isSupported() const override; virtual bool isSupported() const override;
virtual const QString & getName() const override; virtual const QString & getName() const override;
@ -27,7 +27,6 @@ public:
// Stereo specific methods // Stereo specific methods
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override;
virtual void resetSensors() override; virtual void resetSensors() override;
virtual glm::mat4 getEyePose(Eye eye) const override; virtual glm::mat4 getEyePose(Eye eye) const override;

View file

@ -10,14 +10,19 @@
#include <QApplication> #include <QApplication>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QScreen>
#include <GlWindow.h> #include <GlWindow.h>
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include <MatrixStack.h> #include <MatrixStack.h>
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include <plugins/PluginContainer.h>
const QString SideBySideStereoDisplayPlugin::NAME("SBS Stereo Display"); const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
static const QString MENU_PATH = "Display";
static const QString FULLSCREEN = "Fullscreen";
const QString & SideBySideStereoDisplayPlugin::getName() const { const QString & SideBySideStereoDisplayPlugin::getName() const {
return NAME; return NAME;
@ -26,3 +31,20 @@ const QString & SideBySideStereoDisplayPlugin::getName() const {
SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() { SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() {
} }
void SideBySideStereoDisplayPlugin::activate() {
CONTAINER->addMenu(MENU_PATH);
CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN,
[this](bool clicked) {
if (clicked) {
CONTAINER->setFullscreen(getFullscreenTarget());
} else {
CONTAINER->unsetFullscreen();
}
}, true, false);
StereoDisplayPlugin::activate();
}
// FIXME target the screen the window is currently on
QScreen* SideBySideStereoDisplayPlugin::getFullscreenTarget() {
return qApp->primaryScreen();
}

View file

@ -9,11 +9,15 @@
#include "StereoDisplayPlugin.h" #include "StereoDisplayPlugin.h"
class QScreen;
class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin { class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin {
Q_OBJECT Q_OBJECT
public: public:
SideBySideStereoDisplayPlugin(); SideBySideStereoDisplayPlugin();
virtual const QString & getName() const override; virtual const QString& getName() const override;
virtual void activate() override;
private: private:
QScreen* getFullscreenTarget();
static const QString NAME; static const QString NAME;
}; };

View file

@ -29,6 +29,8 @@ const float DEFAULT_IPD = 0.064f;
const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f; const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f;
glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
// FIXME check for mono eye and provide a combined matrix, needed for proper
// culling
// Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating // Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating
// stereo projection matrices. Do NOT use "toe-in", use translation. // stereo projection matrices. Do NOT use "toe-in", use translation.
@ -42,16 +44,18 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje
return glm::translate(baseProjection, vec3(frustumshift, 0, 0)); return glm::translate(baseProjection, vec3(frustumshift, 0, 0));
} }
glm::mat4 StereoDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelview) const { glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const {
float modelviewShift = HALF_DEFAULT_IPD; float modelviewShift = HALF_DEFAULT_IPD;
if (eye == Left) { if (eye == Left) {
modelviewShift = -modelviewShift; modelviewShift = -modelviewShift;
} }
return baseModelview * glm::translate(mat4(), vec3(modelviewShift, 0, 0)); return glm::translate(mat4(), vec3(modelviewShift, 0, 0));
} }
void StereoDisplayPlugin::activate() { void StereoDisplayPlugin::activate() {
WindowOpenGLDisplayPlugin::activate(); WindowOpenGLDisplayPlugin::activate();
CONTAINER->setFullscreen(qApp->primaryScreen()); // FIXME there is a bug in the fullscreen setting, see
// Application::setFullscreen
//CONTAINER->setFullscreen(qApp->primaryScreen());
// FIXME Add menu items // FIXME Add menu items
} }

View file

@ -7,9 +7,9 @@
// //
#pragma once #pragma once
#include "../MainWindowOpenGLDisplayPlugin.h" #include "../WindowOpenGLDisplayPlugin.h"
class StereoDisplayPlugin : public MainWindowOpenGLDisplayPlugin { class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin {
Q_OBJECT Q_OBJECT
public: public:
StereoDisplayPlugin(); StereoDisplayPlugin();
@ -19,6 +19,8 @@ public:
virtual void activate() override; virtual void activate() override;
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override; virtual glm::mat4 getEyePose(Eye eye) const override;
protected:
float _ipd{ 0.064f };
}; };

View file

@ -288,3 +288,18 @@ void Batch::resetStages() {
ADD_COMMAND(resetStages); ADD_COMMAND(resetStages);
} }
void Batch::enableStereo(bool enable) {
_enableStereo = enable;
}
bool Batch::isStereoEnabled() const {
return _enableStereo;
}
void Batch::enableSkybox(bool enable) {
_enableSkybox = enable;
}
bool Batch::isSkyboxEnabled() const {
return _enableSkybox;
}

View file

@ -26,7 +26,7 @@
ProfileRange(const char *name); ProfileRange(const char *name);
~ProfileRange(); ~ProfileRange();
}; };
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); #define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
#else #else
#define PROFILE_RANGE(name) #define PROFILE_RANGE(name)
#endif #endif
@ -47,6 +47,19 @@ public:
~Batch(); ~Batch();
void clear(); void clear();
// Batches may need to override the context level stereo settings
// if they're performing framebuffer copy operations, like the
// deferred lighting resolution mechanism
void enableStereo(bool enable = true);
bool isStereoEnabled() const;
// Stereo batches will pre-translate the view matrix, but this isn't
// appropriate for skyboxes or other things intended to be drawn at
// infinite distance, so provide a mechanism to render in stereo
// without the pre-translation of the view.
void enableSkybox(bool enable = true);
bool isSkyboxEnabled() const;
// Drawcalls // Drawcalls
void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0); void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0);
@ -276,6 +289,9 @@ public:
FramebufferCaches _framebuffers; FramebufferCaches _framebuffers;
QueryCaches _queries; QueryCaches _queries;
bool _enableStereo{ true };
bool _enableSkybox{ false };
protected: protected:
}; };

View file

@ -40,6 +40,18 @@ void Context::render(Batch& batch) {
_backend->render(batch); _backend->render(batch);
} }
void Context::enableStereo(bool enable) {
_backend->enableStereo(enable);
}
void Context::setStereoProjections(const mat4 eyeProjections[2]) {
_backend->setStereoProjections(eyeProjections);
}
void Context::setStereoViews(const mat4 eyeViews[2]) {
_backend->setStereoViews(eyeViews);
}
void Context::syncCache() { void Context::syncCache() {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(__FUNCTION__);
_backend->syncCache(); _backend->syncCache();
@ -49,3 +61,26 @@ void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, cons
_backend->downloadFramebuffer(srcFramebuffer, region, destImage); _backend->downloadFramebuffer(srcFramebuffer, region, destImage);
} }
const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived() const {
_projectionInverse = glm::inverse(_projection);
_viewInverse = glm::inverse(_view);
Mat4 viewUntranslated = _view;
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
_projectionViewUntranslated = _projection * viewUntranslated;
return *this;
}
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo) const {
TransformCamera result = *this;
if (!_stereo._skybox) {
result._view = _stereo._eyeViews[eye] * result._view;
} else {
glm::mat4 skyboxView = _stereo._eyeViews[eye];
skyboxView[3] = vec4(0, 0, 0, 1);
result._view = skyboxView * result._view;
}
result._projection = _stereo._eyeProjections[eye];
result.recomputeDerived();
return result;
}

View file

@ -14,6 +14,8 @@
#include <assert.h> #include <assert.h>
#include <mutex> #include <mutex>
#include <GLMHelpers.h>
#include "Batch.h" #include "Batch.h"
#include "Resource.h" #include "Resource.h"
@ -25,28 +27,58 @@ class QImage;
namespace gpu { namespace gpu {
struct StereoState {
bool _enable{ false };
bool _skybox{ false };
// 0 for left eye, 1 for right eye
uint8_t _pass{ 0 };
mat4 _eyeViews[2];
mat4 _eyeProjections[2];
};
class Backend { class Backend {
public: public:
virtual~ Backend() {}; virtual~ Backend() {};
virtual void render(Batch& batch) = 0; virtual void render(Batch& batch) = 0;
virtual void enableStereo(bool enable) {
_stereo._enable = enable;
}
void setStereoProjections(const mat4 eyeProjections[2]) {
for (int i = 0; i < 2; ++i) {
_stereo._eyeProjections[i] = eyeProjections[i];
}
}
void setStereoViews(const mat4 views[2]) {
for (int i = 0; i < 2; ++i) {
_stereo._eyeViews[i] = views[i];
}
}
virtual void syncCache() = 0; virtual void syncCache() = 0;
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
// UBO class... layout MUST match the layout in TransformCamera.slh
class TransformObject { class TransformObject {
public: public:
Mat4 _model; Mat4 _model;
Mat4 _modelInverse; Mat4 _modelInverse;
}; };
// UBO class... layout MUST match the layout in TransformCamera.slh
class TransformCamera { class TransformCamera {
public: public:
Mat4 _view; Mat4 _view;
Mat4 _viewInverse; mutable Mat4 _viewInverse;
Mat4 _projectionViewUntranslated; mutable Mat4 _projectionViewUntranslated;
Mat4 _projection; Mat4 _projection;
Mat4 _projectionInverse; mutable Mat4 _projectionInverse;
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations. Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
const Backend::TransformCamera& recomputeDerived() const;
TransformCamera getEyeCamera(int eye, const StereoState& stereo) const;
}; };
template< typename T > template< typename T >
@ -113,7 +145,7 @@ public:
} }
protected: protected:
StereoState _stereo;
}; };
class Context { class Context {
@ -136,7 +168,9 @@ public:
~Context(); ~Context();
void render(Batch& batch); void render(Batch& batch);
void enableStereo(bool enable = true);
void setStereoProjections(const mat4 eyeProjections[2]);
void setStereoViews(const mat4 eyeViews[2]);
void syncCache(); void syncCache();
// Downloading the Framebuffer is a synchronous action that is not efficient. // Downloading the Framebuffer is a synchronous action that is not efficient.

View file

@ -0,0 +1,77 @@
//
// Created by Bradley Austin Davis on 2015/08/15
// Copyright 2015 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
//
#pragma once
#ifndef hifi_gpu_Forward_h
#define hifi_gpu_Forward_h
namespace gpu {
class Batch;
class Backend;
class Context;
typedef std::shared_ptr<Context> ContextPointer;
class GPUObject;
typedef int Stamp;
typedef uint32_t uint32;
typedef int32_t int32;
typedef uint16_t uint16;
typedef int16_t int16;
typedef uint8_t uint8;
typedef int8_t int8;
typedef uint8 Byte;
typedef uint32 Offset;
typedef std::vector<Offset> Offsets;
typedef glm::mat4 Mat4;
typedef glm::mat3 Mat3;
typedef glm::vec4 Vec4;
typedef glm::ivec4 Vec4i;
typedef glm::vec3 Vec3;
typedef glm::vec2 Vec2;
typedef glm::ivec2 Vec2i;
typedef glm::uvec2 Vec2u;
class Element;
typedef Element Format;
class Swapchain;
typedef std::shared_ptr<Swapchain> SwapchainPointer;
class Framebuffer;
typedef std::shared_ptr<Framebuffer> FramebufferPointer;
class Pipeline;
typedef std::shared_ptr<Pipeline> PipelinePointer;
typedef std::vector<PipelinePointer> Pipelines;
class Query;
typedef std::shared_ptr<Query> QueryPointer;
typedef std::vector<QueryPointer> Queries;
class Resource;
class Buffer;
typedef std::shared_ptr<Buffer> BufferPointer;
typedef std::vector<BufferPointer> Buffers;
class BufferView;
class Shader;
typedef Shader::Pointer ShaderPointer;
typedef std::vector<ShaderPointer> Shaders;
class State;
typedef std::shared_ptr<State> StatePointer;
typedef std::vector<StatePointer> States;
class Stream;
class BufferStream;
typedef std::shared_ptr<BufferStream> BufferStreamPointer;
class Texture;
class SphericalHarmonics;
typedef std::shared_ptr<SphericalHarmonics> SHPointer;
class Sampler;
class Texture;
typedef std::shared_ptr<Texture> TexturePointer;
typedef std::vector<TexturePointer> Textures;
class TextureView;
typedef std::vector<TextureView> TextureViews;
}
#endif

View file

@ -127,39 +127,29 @@ void GLBackend::renderPassTransfer(Batch& batch) {
const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::Commands::value_type* command = batch.getCommands().data();
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
_transform._cameraTransforms.resize(0); // Reset the transform buffers
_transform._cameraTransforms.push_back(TransformCamera()); _transform._cameras.resize(0);
_transform._cameraOffsets.clear(); _transform._cameraOffsets.clear();
_transform._cameraOffsets.push_back(TransformStageState::Pair(0, 0)); _transform._objects.resize(0);
_transform._objectTransforms.push_back(TransformObject());
_transform._objectOffsets.push_back(TransformStageState::Pair(0, 0));
_transform._objectOffsets.clear(); _transform._objectOffsets.clear();
_transform._objectTransforms.resize(0);
_commandIndex = 0;
preUpdateTransform();
int drawCount = 0;
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
switch (*command) { switch (*command) {
case Batch::COMMAND_draw: case Batch::COMMAND_draw:
case Batch::COMMAND_drawIndexed: case Batch::COMMAND_drawIndexed:
case Batch::COMMAND_drawInstanced: case Batch::COMMAND_drawInstanced:
case Batch::COMMAND_drawIndexedInstanced: case Batch::COMMAND_drawIndexedInstanced:
preUpdateTransform(); _transform.preUpdate(_commandIndex, _stereo);
++drawCount;
break; break;
case Batch::COMMAND_setModelTransform: case Batch::COMMAND_setModelTransform:
case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setViewTransform:
case Batch::COMMAND_setProjectionTransform: case Batch::COMMAND_setProjectionTransform: {
{
CommandCall call = _commandCalls[(*command)]; CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset); (this->*(call))(batch, *offset);
break;
} }
break;
default: default:
break; break;
@ -167,44 +157,31 @@ void GLBackend::renderPassTransfer(Batch& batch) {
command++; command++;
offset++; offset++;
} }
_transform.transfer();
static QByteArray bufferData;
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer);
bufferData.resize(_transform._cameraUboSize * _transform._cameraTransforms.size());
for (size_t i = 0; i < _transform._cameraTransforms.size(); ++i) {
memcpy(bufferData.data() + (_transform._cameraUboSize * i), &_transform._cameraTransforms[i], sizeof(TransformCamera));
}
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer);
bufferData.resize(_transform._objectUboSize * _transform._objectTransforms.size());
for (size_t i = 0; i < _transform._objectTransforms.size(); ++i) {
memcpy(bufferData.data() + (_transform._objectUboSize * i), &_transform._objectTransforms[i], sizeof(TransformObject));
}
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
CHECK_GL_ERROR();
} }
void GLBackend::renderPassDraw(Batch& batch) { void GLBackend::renderPassDraw(Batch& batch) {
_transform._objectsItr = _transform._objectOffsets.begin();
_transform._camerasItr = _transform._cameraOffsets.begin();
const size_t numCommands = batch.getCommands().size(); const size_t numCommands = batch.getCommands().size();
const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::Commands::value_type* command = batch.getCommands().data();
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
switch (*command) { switch (*command) {
// Ignore these commands on this pass, taken care of in the transfer pass // Ignore these commands on this pass, taken care of in the transfer pass
// Note we allow COMMAND_setViewportTransform to occur in both passes
// as it both updates the transform object (and thus the uniforms in the
// UBO) as well as executes the actual viewport call
case Batch::COMMAND_setModelTransform: case Batch::COMMAND_setModelTransform:
case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setViewTransform:
case Batch::COMMAND_setProjectionTransform: case Batch::COMMAND_setProjectionTransform:
break; break;
default: default: {
{
CommandCall call = _commandCalls[(*command)]; CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset); (this->*(call))(batch, *offset);
break;
} }
break;
} }
command++; command++;
@ -213,8 +190,33 @@ void GLBackend::renderPassDraw(Batch& batch) {
} }
void GLBackend::render(Batch& batch) { void GLBackend::render(Batch& batch) {
renderPassTransfer(batch); _stereo._skybox = batch.isSkyboxEnabled();
renderPassDraw(batch); // Allow the batch to override the rendering stereo settings
// for things like full framebuffer copy operations (deferred lighting passes)
bool savedStereo = _stereo._enable;
if (!batch.isStereoEnabled()) {
_stereo._enable = false;
}
{
PROFILE_RANGE("Transfer");
renderPassTransfer(batch);
}
{
PROFILE_RANGE(_stereo._enable ? "LeftRender" : "Render");
renderPassDraw(batch);
}
if (_stereo._enable) {
PROFILE_RANGE("RightRender");
_stereo._pass = 1;
renderPassDraw(batch);
_stereo._pass = 0;
}
// Restore the saved stereo state for the next batch
_stereo._enable = savedStereo;
} }
bool GLBackend::checkGLError(const char* name) { bool GLBackend::checkGLError(const char* name) {

View file

@ -310,19 +310,22 @@ protected:
void killTransform(); void killTransform();
// Synchronize the state cache of this Backend with the actual real state of the GL Context // Synchronize the state cache of this Backend with the actual real state of the GL Context
void syncTransformStateCache(); void syncTransformStateCache();
void updateTransform(); void updateTransform() const;
void preUpdateTransform();
void resetTransformStage(); void resetTransformStage();
struct TransformStageState {
TransformObject _transformObject;
TransformCamera _transformCamera;
std::vector<TransformObject> _objectTransforms; struct TransformStageState {
std::vector<TransformCamera> _cameraTransforms; using TransformObjects = std::vector<TransformObject>;
using TransformCameras = std::vector<TransformCamera>;
TransformObject _object;
TransformCamera _camera;
TransformObjects _objects;
TransformCameras _cameras;
size_t _cameraUboSize{ 0 }; size_t _cameraUboSize{ 0 };
size_t _objectUboSize{ 0 }; size_t _objectUboSize{ 0 };
GLuint _transformObjectBuffer{ 0 }; GLuint _objectBuffer{ 0 };
GLuint _transformCameraBuffer{ 0 }; GLuint _cameraBuffer{ 0 };
Transform _model; Transform _model;
Transform _view; Transform _view;
Mat4 _projection; Mat4 _projection;
@ -336,6 +339,12 @@ protected:
using List = std::list<Pair>; using List = std::list<Pair>;
List _cameraOffsets; List _cameraOffsets;
List _objectOffsets; List _objectOffsets;
mutable List::const_iterator _objectsItr;
mutable List::const_iterator _camerasItr;
void preUpdate(size_t commandIndex, const StereoState& stereo);
void update(size_t commandIndex, const StereoState& stereo) const;
void transfer() const;
} _transform; } _transform;
int32_t _uboAlignment{ 0 }; int32_t _uboAlignment{ 0 };

View file

@ -198,6 +198,9 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) {
} }
void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) { void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
if (_stereo._enable && !_pipeline._stateCache.scissorEnable) {
qWarning("Clear without scissor in stereo mode");
}
uint32 masks = batch._params[paramOffset + 7]._uint; uint32 masks = batch._params[paramOffset + 7]._uint;
Vec4 color; Vec4 color;

View file

@ -768,6 +768,12 @@ void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) {
Vec4i rect; Vec4i rect;
memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
if (_stereo._enable) {
rect.z /= 2;
if (_stereo._pass) {
rect.x += rect.z;
}
}
glScissor(rect.x, rect.y, rect.z, rect.w); glScissor(rect.x, rect.y, rect.z, rect.w);
(void) CHECK_GL_ERROR(); (void) CHECK_GL_ERROR();
} }

View file

@ -33,16 +33,26 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) { void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
ivec4& vp = _transform._viewport;
// Where we assign the GL viewport // Where we assign the GL viewport
glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w); if (_stereo._enable) {
vp.z /= 2;
if (_stereo._pass) {
vp.x += vp.z;
}
int i = 0;
}
glViewport(vp.x, vp.y, vp.z, vp.w);
// The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall // The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall
_transform._invalidViewport = true; _transform._invalidViewport = true;
} }
void GLBackend::initTransform() { void GLBackend::initTransform() {
glGenBuffers(1, &_transform._transformObjectBuffer); glGenBuffers(1, &_transform._objectBuffer);
glGenBuffers(1, &_transform._transformCameraBuffer); glGenBuffers(1, &_transform._cameraBuffer);
size_t cameraSize = sizeof(TransformCamera); size_t cameraSize = sizeof(TransformCamera);
while (_transform._cameraUboSize < cameraSize) { while (_transform._cameraUboSize < cameraSize) {
_transform._cameraUboSize += _uboAlignment; _transform._cameraUboSize += _uboAlignment;
@ -54,8 +64,8 @@ void GLBackend::initTransform() {
} }
void GLBackend::killTransform() { void GLBackend::killTransform() {
glDeleteBuffers(1, &_transform._transformObjectBuffer); glDeleteBuffers(1, &_transform._objectBuffer);
glDeleteBuffers(1, &_transform._transformCameraBuffer); glDeleteBuffers(1, &_transform._cameraBuffer);
} }
void GLBackend::syncTransformStateCache() { void GLBackend::syncTransformStateCache() {
@ -72,73 +82,98 @@ void GLBackend::syncTransformStateCache() {
_transform._model.setIdentity(); _transform._model.setIdentity();
} }
void GLBackend::preUpdateTransform() { void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) {
// Check all the dirty flags and update the state accordingly // Check all the dirty flags and update the state accordingly
if (_transform._invalidViewport) { if (_invalidViewport) {
_transform._transformCamera._viewport = glm::vec4(_transform._viewport); _camera._viewport = glm::vec4(_viewport);
} }
if (_transform._invalidProj) { if (_invalidProj) {
_transform._transformCamera._projection = _transform._projection; _camera._projection = _projection;
_transform._transformCamera._projectionInverse = glm::inverse(_transform._projection);
} }
if (_transform._invalidView) { if (_invalidView) {
_transform._view.getInverseMatrix(_transform._transformCamera._view); _view.getInverseMatrix(_camera._view);
_transform._view.getMatrix(_transform._transformCamera._viewInverse);
} }
if (_transform._invalidModel) { if (_invalidModel) {
_transform._model.getMatrix(_transform._transformObject._model); _model.getMatrix(_object._model);
_transform._model.getInverseMatrix(_transform._transformObject._modelInverse); _model.getInverseMatrix(_object._modelInverse);
} }
if (_transform._invalidView || _transform._invalidProj) { if (_invalidView || _invalidProj || _invalidViewport) {
Mat4 viewUntranslated = _transform._transformCamera._view; size_t offset = _cameraUboSize * _cameras.size();
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f); if (stereo._enable) {
_transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated; _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
for (int i = 0; i < 2; ++i) {
_cameras.push_back(_camera.getEyeCamera(i, stereo));
}
} else {
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
_cameras.push_back(_camera.recomputeDerived());
}
} }
if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) { if (_invalidModel) {
_transform._cameraOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._cameraUboSize * _transform._cameraTransforms.size())); size_t offset = _objectUboSize * _objects.size();
_transform._cameraTransforms.push_back(_transform._transformCamera); _objectOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
} _objects.push_back(_object);
if (_transform._invalidModel) {
_transform._objectOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._objectUboSize * _transform._objectTransforms.size()));
_transform._objectTransforms.push_back(_transform._transformObject);
} }
// Flags are clean // Flags are clean
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false; _invalidView = _invalidProj = _invalidModel = _invalidViewport = false;
} }
void GLBackend::updateTransform() { void GLBackend::TransformStageState::transfer() const {
static QByteArray bufferData;
glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
bufferData.resize(_cameraUboSize * _cameras.size());
for (size_t i = 0; i < _cameras.size(); ++i) {
memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
}
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
bufferData.resize(_objectUboSize * _objects.size());
for (size_t i = 0; i < _objects.size(); ++i) {
memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
}
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
CHECK_GL_ERROR();
}
void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const {
int offset = -1; int offset = -1;
while (!_transform._objectOffsets.empty() && _commandIndex >= _transform._objectOffsets.front().first) { while ((_objectsItr != _objectOffsets.end()) && (commandIndex >= (*_objectsItr).first)) {
offset = _transform._objectOffsets.front().second; offset = (*_objectsItr).second;
_transform._objectOffsets.pop_front(); ++_objectsItr;
} }
if (offset >= 0) { if (offset >= 0) {
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT,
_transform._transformObjectBuffer, _objectBuffer, offset, sizeof(Backend::TransformObject));
offset, sizeof(Backend::TransformObject));
} }
offset = -1; offset = -1;
while (!_transform._cameraOffsets.empty() && _commandIndex >= _transform._cameraOffsets.front().first) { while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) {
offset = _transform._cameraOffsets.front().second; offset = (*_camerasItr).second;
_transform._cameraOffsets.pop_front(); ++_camerasItr;
} }
if (offset >= 0) { if (offset >= 0) {
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, // We include both camera offsets for stereo
_transform._transformCameraBuffer, if (stereo._enable && stereo._pass) {
offset, sizeof(Backend::TransformCamera)); offset += _cameraUboSize;
}
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT,
_cameraBuffer, offset, sizeof(Backend::TransformCamera));
} }
(void)CHECK_GL_ERROR(); (void)CHECK_GL_ERROR();
} }
void GLBackend::updateTransform() const {
_transform.update(_commandIndex, _stereo);
}
void GLBackend::resetTransformStage() { void GLBackend::resetTransformStage() {
} }

View file

@ -189,7 +189,6 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint
renderHand(rightHand, batch, RIGHT_HAND); renderHand(rightHand, batch, RIGHT_HAND);
} }
args->_context->syncCache();
args->_context->render(batch); args->_context->render(batch);
} }
} }

View file

@ -246,6 +246,5 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
// Ready to render // Ready to render
args->_context->syncCache();
args->_context->render((batch)); args->_context->render((batch));
} }

View file

@ -225,6 +225,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
void DeferredLightingEffect::prepare(RenderArgs* args) { void DeferredLightingEffect::prepare(RenderArgs* args) {
gpu::Batch batch; gpu::Batch batch;
batch.enableStereo(false);
batch.setStateScissorRect(args->_viewport); batch.setStateScissorRect(args->_viewport);
@ -244,6 +245,9 @@ gpu::FramebufferPointer _copyFBO;
void DeferredLightingEffect::render(RenderArgs* args) { void DeferredLightingEffect::render(RenderArgs* args) {
gpu::Batch batch; gpu::Batch batch;
// Framebuffer copy operations cannot function as multipass stereo operations.
batch.enableStereo(false);
// perform deferred lighting, rendering to free fbo // perform deferred lighting, rendering to free fbo
auto framebufferCache = DependencyManager::get<FramebufferCache>(); auto framebufferCache = DependencyManager::get<FramebufferCache>();
@ -555,6 +559,7 @@ void DeferredLightingEffect::render(RenderArgs* args) {
void DeferredLightingEffect::copyBack(RenderArgs* args) { void DeferredLightingEffect::copyBack(RenderArgs* args) {
gpu::Batch batch; gpu::Batch batch;
batch.enableStereo(false);
auto framebufferCache = DependencyManager::get<FramebufferCache>(); auto framebufferCache = DependencyManager::get<FramebufferCache>();
QSize framebufferSize = framebufferCache->getFrameBufferSize(); QSize framebufferSize = framebufferCache->getFrameBufferSize();

View file

@ -92,17 +92,17 @@ void Environment::resetToDefault() {
_data[QUuid()][0]; _data[QUuid()][0];
} }
void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera) { void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& viewFrustum) {
// get the lock for the duration of the call // get the lock for the duration of the call
QMutexLocker locker(&_mutex); QMutexLocker locker(&_mutex);
if (_environmentIsOverridden) { if (_environmentIsOverridden) {
renderAtmosphere(batch, camera, _overrideData); renderAtmosphere(batch, viewFrustum, _overrideData);
} else { } else {
foreach (const ServerData& serverData, _data) { foreach (const ServerData& serverData, _data) {
// TODO: do something about EnvironmentData // TODO: do something about EnvironmentData
foreach (const EnvironmentData& environmentData, serverData) { foreach (const EnvironmentData& environmentData, serverData) {
renderAtmosphere(batch, camera, environmentData); renderAtmosphere(batch, viewFrustum, environmentData);
} }
} }
} }
@ -196,15 +196,25 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
return found; return found;
} }
void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& viewFrustum, const EnvironmentData& data) {
glm::vec3 center = data.getAtmosphereCenter(); glm::vec3 center = data.getAtmosphereCenter();
// transform the model transform to the center of our atmosphere
Transform transform; Transform transform;
transform.setTranslation(center); transform.setTranslation(center);
batch.setModelTransform(transform); batch.setModelTransform(transform);
glm::vec3 relativeCameraPos = camera.getPosition() - center; // Make sure our view and projection transforms are correct for our viewFrustum
Transform viewTransform;
viewFrustum.evalViewTransform(viewTransform);
batch.setViewTransform(viewTransform);
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
batch.setProjectionTransform(projMat);
glm::vec3 relativeCameraPos = viewFrustum.getPosition() - center;
float height = glm::length(relativeCameraPos); float height = glm::length(relativeCameraPos);
// use the appropriate shader depending on whether we're inside or outside // use the appropriate shader depending on whether we're inside or outside
@ -212,11 +222,11 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const
if (height < data.getAtmosphereOuterRadius()) { if (height < data.getAtmosphereOuterRadius()) {
batch.setPipeline(_skyFromAtmosphereProgram); batch.setPipeline(_skyFromAtmosphereProgram);
locations = _skyFromAtmosphereUniformLocations; locations = _skyFromAtmosphereUniformLocations;
} else { } else {
batch.setPipeline(_skyFromSpaceProgram); batch.setPipeline(_skyFromSpaceProgram);
locations = _skyFromSpaceUniformLocations; locations = _skyFromSpaceUniformLocations;
} }
// the constants here are from Sean O'Neil's GPU Gems entry // the constants here are from Sean O'Neil's GPU Gems entry
// (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp

View file

@ -29,7 +29,7 @@ public:
void init(); void init();
void resetToDefault(); void resetToDefault();
void renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera); void renderAtmospheres(gpu::Batch& batch, ViewFrustum& viewFrustum);
void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; } void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; }
void endOverride() { _environmentIsOverridden = false; } void endOverride() { _environmentIsOverridden = false; }
@ -41,7 +41,7 @@ private:
bool findCapsulePenetration(const glm::vec3& start, bool findCapsulePenetration(const glm::vec3& start,
const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated
void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data); void renderAtmosphere(gpu::Batch& batch, ViewFrustum& viewFrustum, const EnvironmentData& data);
bool _initialized; bool _initialized;

View file

@ -35,6 +35,7 @@ void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderCon
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor(); auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
gpu::Batch batch; gpu::Batch batch;
batch.enableStereo(false);
batch.setFramebuffer(nullptr); batch.setFramebuffer(nullptr);
batch.setFramebuffer(primaryFbo); batch.setFramebuffer(primaryFbo);
@ -159,6 +160,8 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
gpu::Batch batch; gpu::Batch batch;
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
args->_batch = &batch; args->_batch = &batch;
renderContext->_numDrawnOpaqueItems = inItems.size(); renderContext->_numDrawnOpaqueItems = inItems.size();
@ -188,6 +191,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
gpu::Batch batch; gpu::Batch batch;
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
args->_batch = &batch; args->_batch = &batch;
renderContext->_numDrawnTransparentItems = inItems.size(); renderContext->_numDrawnTransparentItems = inItems.size();
@ -247,30 +252,42 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
renderContext->_numFeedOverlay3DItems = inItems.size(); renderContext->_numFeedOverlay3DItems = inItems.size();
renderContext->_numDrawnOverlay3DItems = inItems.size(); renderContext->_numDrawnOverlay3DItems = inItems.size();
RenderArgs* args = renderContext->args;
gpu::Batch batch;
args->_batch = &batch;
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
batch.setPipeline(getOpaquePipeline());
batch.setResourceTexture(0, args->_whiteTexture);
if (!inItems.empty()) { if (!inItems.empty()) {
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); RenderArgs* args = renderContext->args;
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
}
args->_context->render((*args->_batch)); // Clear the framebuffer without stereo
args->_batch = nullptr; // Needs to be distinct from the other batch because using the clear call
args->_whiteTexture.reset(); // while stereo is enabled triggers a warning
{
gpu::Batch batch;
batch.enableStereo(false);
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
args->_context->render(batch);
}
// Render the items
{
gpu::Batch batch;
args->_batch = &batch;
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
batch.setPipeline(getOpaquePipeline());
batch.setResourceTexture(0, args->_whiteTexture);
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
args->_context->render((*args->_batch));
args->_batch = nullptr;
args->_whiteTexture.reset();
}
}
} }

View file

@ -161,8 +161,5 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
batch.draw(gpu::TRIANGLES, 24, 0); batch.draw(gpu::TRIANGLES, 24, 0);
} }
// Before rendering the batch make sure we re in sync with gl state args->_context->render(batch);
args->_context->syncCache();
renderContext->args->_context->syncCache();
args->_context->render((batch));
} }

View file

@ -258,6 +258,9 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
} }
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
gpu::Batch batch; gpu::Batch batch;
batch.enableSkybox(true);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
args->_batch = &batch; args->_batch = &batch;
glm::mat4 projMat; glm::mat4 projMat;
@ -271,9 +274,6 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
renderItems(sceneContext, renderContext, inItems); renderItems(sceneContext, renderContext, inItems);
args->_context->render((*args->_batch)); args->_context->render((*args->_batch));
args->_batch = nullptr; args->_batch = nullptr;
// Force the context sync
args->_context->syncCache();
} }
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {