Merge pull request #8164 from SamGondelman/macHMDPreview

Added Mac preview image if VSync is enabled
This commit is contained in:
Brad Hefta-Gaub 2016-07-07 09:01:40 -07:00 committed by GitHub
commit abcd1e0062
7 changed files with 152 additions and 36 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View file

@ -16,6 +16,9 @@
#include <QtOpenGL/QGLWidget> #include <QtOpenGL/QGLWidget>
#include <QtGui/QImage> #include <QtGui/QImage>
#if defined(Q_OS_MAC)
#include <OpenGL/CGLCurrent.h>
#endif
#include <gl/QOpenGLContextWrapper.h> #include <gl/QOpenGLContextWrapper.h>
#include <gpu/Texture.h> #include <gpu/Texture.h>
#include <gl/GLWidget.h> #include <gl/GLWidget.h>
@ -612,8 +615,14 @@ void OpenGLDisplayPlugin::enableVsync(bool enable) {
if (!_vsyncSupported) { if (!_vsyncSupported) {
return; return;
} }
#ifdef Q_OS_WIN #if defined(Q_OS_WIN)
wglSwapIntervalEXT(enable ? 1 : 0); wglSwapIntervalEXT(enable ? 1 : 0);
#elif defined(Q_OS_MAC)
GLint interval = enable ? 1 : 0;
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
#else
// TODO: Fill in for linux
return;
#endif #endif
} }
@ -621,9 +630,14 @@ bool OpenGLDisplayPlugin::isVsyncEnabled() {
if (!_vsyncSupported) { if (!_vsyncSupported) {
return true; return true;
} }
#ifdef Q_OS_WIN #if defined(Q_OS_WIN)
return wglGetSwapIntervalEXT() != 0; return wglGetSwapIntervalEXT() != 0;
#elif defined(Q_OS_MAC)
GLint interval;
CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
return interval != 0;
#else #else
// TODO: Fill in for linux
return true; return true;
#endif #endif
} }

View file

@ -21,6 +21,11 @@
#include <gl/GLWidget.h> #include <gl/GLWidget.h>
#include <shared/NsightHelpers.h> #include <shared/NsightHelpers.h>
#include <gpu/DrawUnitQuadTexcoord_vert.h>
#include <gpu/DrawTexture_frag.h>
#include <PathUtils.h>
#include "../Logging.h" #include "../Logging.h"
#include "../CompositorHelper.h" #include "../CompositorHelper.h"
@ -58,9 +63,33 @@ bool HmdDisplayPlugin::internalActivate() {
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]); _eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
}); });
if (_previewTextureID == 0) {
QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png");
if (!previewTexture.isNull()) {
glGenTextures(1, &_previewTextureID);
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, previewTexture.width(), previewTexture.height(), 0,
GL_BGRA, GL_UNSIGNED_BYTE, previewTexture.mirrored(false, true).bits());
using namespace oglplus;
Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear);
Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear);
glBindTexture(GL_TEXTURE_2D, 0);
_previewAspect = ((float)previewTexture.width())/((float)previewTexture.height());
_firstPreview = true;
}
}
return Parent::internalActivate(); return Parent::internalActivate();
} }
void HmdDisplayPlugin::internalDeactivate() {
if (_previewTextureID != 0) {
glDeleteTextures(1, &_previewTextureID);
_previewTextureID = 0;
}
Parent::internalDeactivate();
}
static const char * REPROJECTION_VS = R"VS(#version 410 core static const char * REPROJECTION_VS = R"VS(#version 410 core
in vec3 Position; in vec3 Position;
@ -196,6 +225,7 @@ static ProgramPtr getReprojectionProgram() {
} }
#endif #endif
static GLint PREVIEW_TEXTURE_LOCATION = -1;
static const char * LASER_VS = R"VS(#version 410 core static const char * LASER_VS = R"VS(#version 410 core
uniform mat4 mvp = mat4(1); uniform mat4 mvp = mat4(1);
@ -227,14 +257,24 @@ void main() {
void HmdDisplayPlugin::customizeContext() { void HmdDisplayPlugin::customizeContext() {
Parent::customizeContext(); Parent::customizeContext();
// Only enable mirroring if we know vsync is disabled // Only enable mirroring if we know vsync is disabled
// On Mac, this won't work due to how the contexts are handled, so don't try
#if !defined(Q_OS_MAC)
enableVsync(false); enableVsync(false);
#endif
_enablePreview = !isVsyncEnabled(); _enablePreview = !isVsyncEnabled();
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO); _sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
compileProgram(_laserProgram, LASER_VS, LASER_FS);
_laserGeometry = loadLaser(_laserProgram);
compileProgram(_reprojectionProgram, REPROJECTION_VS, REPROJECTION_FS);
using namespace oglplus; using namespace oglplus;
if (!_enablePreview) {
const std::string version("#version 410 core\n");
compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag);
PREVIEW_TEXTURE_LOCATION = Uniform<int>(*_previewProgram, "colorMap").Location();
}
compileProgram(_laserProgram, LASER_VS, LASER_FS);
_laserGeometry = loadLaser(_laserProgram);
compileProgram(_reprojectionProgram, REPROJECTION_VS, REPROJECTION_FS);
REPROJECTION_MATRIX_LOCATION = Uniform<glm::mat3>(*_reprojectionProgram, "reprojection").Location(); REPROJECTION_MATRIX_LOCATION = Uniform<glm::mat3>(*_reprojectionProgram, "reprojection").Location();
INVERSE_PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "inverseProjections").Location(); INVERSE_PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "inverseProjections").Location();
PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "projections").Location(); PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "projections").Location();
@ -243,6 +283,7 @@ void HmdDisplayPlugin::customizeContext() {
void HmdDisplayPlugin::uncustomizeContext() { void HmdDisplayPlugin::uncustomizeContext() {
_sphereSection.reset(); _sphereSection.reset();
_compositeFramebuffer.reset(); _compositeFramebuffer.reset();
_previewProgram.reset();
_reprojectionProgram.reset(); _reprojectionProgram.reset();
_laserProgram.reset(); _laserProgram.reset();
_laserGeometry.reset(); _laserGeometry.reset();
@ -335,30 +376,32 @@ void HmdDisplayPlugin::internalPresent() {
hmdPresent(); hmdPresent();
// screen preview mirroring // screen preview mirroring
auto window = _container->getPrimaryWidget();
auto devicePixelRatio = window->devicePixelRatio();
auto windowSize = toGlm(window->size());
windowSize *= devicePixelRatio;
float windowAspect = aspect(windowSize);
float sceneAspect = _enablePreview ? aspect(_renderTargetSize) : _previewAspect;
if (_enablePreview && _monoPreview) {
sceneAspect /= 2.0f;
}
float aspectRatio = sceneAspect / windowAspect;
uvec2 targetViewportSize = windowSize;
if (aspectRatio < 1.0f) {
targetViewportSize.x *= aspectRatio;
} else {
targetViewportSize.y /= aspectRatio;
}
uvec2 targetViewportPosition;
if (targetViewportSize.x < windowSize.x) {
targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2;
} else if (targetViewportSize.y < windowSize.y) {
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
}
if (_enablePreview) { if (_enablePreview) {
auto window = _container->getPrimaryWidget();
auto windowSize = toGlm(window->size());
float windowAspect = aspect(windowSize);
float sceneAspect = aspect(_renderTargetSize);
if (_monoPreview) {
sceneAspect /= 2.0f;
}
float aspectRatio = sceneAspect / windowAspect;
uvec2 targetViewportSize = windowSize;
if (aspectRatio < 1.0f) {
targetViewportSize.x *= aspectRatio;
} else {
targetViewportSize.y /= aspectRatio;
}
uvec2 targetViewportPosition;
if (targetViewportSize.x < windowSize.x) {
targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2;
} else if (targetViewportSize.y < windowSize.y) {
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
}
using namespace oglplus; using namespace oglplus;
Context::Clear().ColorBuffer(); Context::Clear().ColorBuffer();
auto sourceSize = _compositeFramebuffer->size; auto sourceSize = _compositeFramebuffer->size;
@ -373,6 +416,21 @@ void HmdDisplayPlugin::internalPresent() {
BufferSelectBit::ColorBuffer, BlitFilter::Nearest); BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
}); });
swapBuffers(); swapBuffers();
} else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) {
useProgram(_previewProgram);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y);
glUniform1i(PREVIEW_TEXTURE_LOCATION, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
swapBuffers();
_firstPreview = false;
_prevWindowSize = windowSize;
_prevDevicePixelRatio = devicePixelRatio;
} }
postPreview(); postPreview();

View file

@ -40,6 +40,7 @@ protected:
virtual void updatePresentPose(); virtual void updatePresentPose();
bool internalActivate() override; bool internalActivate() override;
void internalDeactivate() override;
void compositeScene() override; void compositeScene() override;
void compositeOverlay() override; void compositeOverlay() override;
void compositePointer() override; void compositePointer() override;
@ -89,8 +90,17 @@ private:
bool _enablePreview { false }; bool _enablePreview { false };
bool _monoPreview { true }; bool _monoPreview { true };
bool _enableReprojection { true }; bool _enableReprojection { true };
ShapeWrapperPtr _sphereSection; bool _firstPreview { true };
ProgramPtr _previewProgram;
float _previewAspect { 0 };
GLuint _previewTextureID { 0 };
glm::uvec2 _prevWindowSize { 0, 0 };
qreal _prevDevicePixelRatio { 0 };
ProgramPtr _reprojectionProgram; ProgramPtr _reprojectionProgram;
ShapeWrapperPtr _sphereSection;
ProgramPtr _laserProgram; ProgramPtr _laserProgram;
ShapeWrapperPtr _laserGeometry; ShapeWrapperPtr _laserGeometry;
}; };

View file

@ -20,6 +20,7 @@
#include <OpenGL/gl.h> #include <OpenGL/gl.h>
#include <OpenGL/glext.h> #include <OpenGL/glext.h>
#include <OpenGL/OpenGL.h>
#endif #endif

View file

@ -47,13 +47,16 @@ void GLWidget::initializeGL() {
// Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate.
setAutoBufferSwap(false); setAutoBufferSwap(false);
// TODO: write the proper code for linux
makeCurrent(); makeCurrent();
#if defined(Q_OS_WIN)
if (isValid() && context() && context()->contextHandle()) { if (isValid() && context() && context()->contextHandle()) {
_vsyncSupported = context()->contextHandle()->hasExtension("WGL_EXT_swap_control");; #if defined(Q_OS_WIN)
} _vsyncSupported = context()->contextHandle()->hasExtension("WGL_EXT_swap_control");
#elif defined(Q_OS_MAC)
_vsyncSupported = true;
#else
// TODO: write the proper code for linux
#endif #endif
}
} }
void GLWidget::paintEvent(QPaintEvent* event) { void GLWidget::paintEvent(QPaintEvent* event) {

View file

@ -68,18 +68,48 @@ bool OculusLegacyDisplayPlugin::isSupported() const {
} }
auto hmd = ovrHmd_Create(0); auto hmd = ovrHmd_Create(0);
// The Oculus SDK seems to have trouble finding the right screen sometimes, so we have to guess
// Guesses, in order of best match:
// - resolution and position match
// - resolution and one component of position match
// - resolution matches
// - position matches
// If it still picks the wrong screen, you'll have to mess with your monitor configuration
QList<int> matches({ -1, -1, -1, -1 });
if (hmd) { if (hmd) {
QPoint targetPosition{ hmd->WindowsPos.x, hmd->WindowsPos.y }; QPoint targetPosition{ hmd->WindowsPos.x, hmd->WindowsPos.y };
QSize targetResolution{ hmd->Resolution.w, hmd->Resolution.h };
auto screens = qApp->screens(); auto screens = qApp->screens();
for(int i = 0; i < screens.size(); ++i) { for(int i = 0; i < screens.size(); ++i) {
auto screen = screens[i]; auto screen = screens[i];
QPoint position = screen->geometry().topLeft(); QPoint position = screen->geometry().topLeft();
if (position == targetPosition) { QSize resolution = screen->geometry().size();
_hmdScreen = i;
break; if (position == targetPosition && resolution == targetResolution) {
matches[0] = i;
} else if ((position.x() == targetPosition.x() || position.y() == targetPosition.y()) &&
resolution == targetResolution) {
matches[1] = i;
} else if (resolution == targetResolution) {
matches[2] = i;
} else if (position == targetPosition) {
matches[3] = i;
} }
} }
} }
for (int screen : matches) {
if (screen != -1) {
_hmdScreen = screen;
break;
}
}
if (_hmdScreen == -1) {
qDebug() << "Could not find Rift screen";
result = false;
}
ovr_Shutdown(); ovr_Shutdown();
return result; return result;