mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 19:59:28 +02:00
Merge pull request #8164 from SamGondelman/macHMDPreview
Added Mac preview image if VSync is enabled
This commit is contained in:
commit
abcd1e0062
7 changed files with 152 additions and 36 deletions
BIN
interface/resources/images/preview.png
Normal file
BIN
interface/resources/images/preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue