Fixing screenshot functionality

This commit is contained in:
Brad Davis 2015-12-03 13:00:36 -08:00
parent b9ace94ff1
commit d8bb9f8d18
6 changed files with 105 additions and 17 deletions

View file

@ -44,6 +44,9 @@
#include <QtNetwork/QNetworkDiskCache>
#include <gl/Config.h>
#include <QtGui/QOpenGLContext>
#include <AccountManager.h>
#include <AddressManager.h>
#include <ApplicationVersion.h>
@ -150,8 +153,6 @@
#include "InterfaceParentFinder.h"
#include <gpu/GLBackend.h>
#include <QtGui/QOpenGLContext>
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
@ -1091,7 +1092,6 @@ void Application::paintGL() {
// update fps once a second
if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) {
_fps = _framesPerSecond.getAverage();
qDebug() << QString::number(_fps, 'g', 4);
_lastFramesPerSecondUpdate = now;
}
@ -1344,6 +1344,7 @@ void Application::paintGL() {
}
// Overlay Composition, needs to occur after screen space effects have completed
// FIXME migrate composition into the display plugins
{
PROFILE_RANGE(__FUNCTION__ "/compositor");
PerformanceTimer perfTimer("compositor");
@ -1372,7 +1373,6 @@ void Application::paintGL() {
{
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
PerformanceTimer perfTimer("pluginOutput");
uint64_t displayStart = usecTimestampNow();
auto primaryFramebuffer = framebufferCache->getPrimaryFramebuffer();
auto scratchFramebuffer = framebufferCache->getFramebuffer();
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
@ -1386,12 +1386,18 @@ void Application::paintGL() {
});
auto finalTexturePointer = scratchFramebuffer->getRenderBuffer(0);
GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
Q_ASSERT(0 != finalTexture);
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
uint64_t displayStart = usecTimestampNow();
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
{
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
PerformanceTimer perfTimer("pluginSubmitScene");
displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
}
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
uint64_t displayEnd = usecTimestampNow();
@ -4499,13 +4505,12 @@ void Application::toggleLogDialog() {
}
void Application::takeSnapshot() {
#if 0
QMediaPlayer* player = new QMediaPlayer();
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
player->play();
QString fileName = Snapshot::saveSnapshot(_glWidget->grabFrameBuffer());
QString fileName = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
AccountManager& accountManager = AccountManager::getInstance();
if (!accountManager.isLoggedIn()) {
@ -4516,7 +4521,6 @@ void Application::takeSnapshot() {
_snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget);
}
_snapshotShareDialog->show();
#endif
}
float Application::getRenderResolutionScale() const {
@ -4728,6 +4732,7 @@ void Application::updateDisplayMode() {
bool first = true;
foreach(auto displayPlugin, displayPlugins) {
addDisplayPluginToMenu(displayPlugin, first);
// This must be a queued connection to avoid a deadlock
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
this, &Application::paintGL, Qt::QueuedConnection);

View file

@ -9,7 +9,9 @@
//
#include "NullDisplayPlugin.h"
#include <QtGui/QImage>
#include <plugins/PluginContainer.h>
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
const QString & NullDisplayPlugin::getName() const {
@ -33,3 +35,7 @@ void NullDisplayPlugin::submitOverlayTexture(uint32_t overlayTexture, const glm:
}
void NullDisplayPlugin::stop() {}
QImage NullDisplayPlugin::getScreenshot() const {
return QImage();
}

View file

@ -21,7 +21,7 @@ public:
virtual bool hasFocus() const override;
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
virtual QImage getScreenshot() const override;
private:
static const QString NAME;
};

View file

@ -7,12 +7,15 @@
//
#include "OpenGLDisplayPlugin.h"
#include <condition_variable>
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QOpenGLContext>
#include <QtGui/QImage>
#include <NumericalConstants.h>
#include <DependencyManager.h>
@ -23,7 +26,9 @@
class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex;
using Condition = std::condition_variable;
using Lock = std::unique_lock<Mutex>;
friend class OpenGLDisplayPlugin;
public:
@ -40,6 +45,25 @@ public:
virtual void run() override {
Q_ASSERT(_context);
while (!_shutdown) {
if (_pendingMainThreadOperation) {
{
Lock lock(_mutex);
// Move the context to the main thread
_context->moveToThread(qApp->thread());
_widgetContext->moveToThread(qApp->thread());
_pendingMainThreadOperation = false;
// Release the main thread to do it's action
_condition.notify_one();
}
{
// Main thread does it's thing while we wait on the lock to release
Lock lock(_mutex);
_condition.wait(lock, [&] { return _finishedMainThreadOperation; });
}
}
// Check before lock
if (_newPlugin != nullptr) {
Lock lock(_mutex);
@ -69,17 +93,43 @@ public:
}
_context->doneCurrent();
_widgetContext->moveToThread(qApp->thread());
_context->moveToThread(qApp->thread());
}
void withMainThreadContext(std::function<void()> f) {
// Signal to the thread that there is work to be done on the main thread
Lock lock(_mutex);
_pendingMainThreadOperation = true;
_finishedMainThreadOperation = false;
_condition.wait(lock, [&] { return !_pendingMainThreadOperation; });
_widgetContext->makeCurrent();
f();
_widgetContext->doneCurrent();
// restore control of the context to the presentation thread and signal
// the end of the operation
_widgetContext->moveToThread(this);
_context->moveToThread(this);
_finishedMainThreadOperation = true;
lock.unlock();
_condition.notify_one();
}
private:
bool _shutdown { false };
Mutex _mutex;
// Used to allow the main thread to perform context operations
Condition _condition;
bool _pendingMainThreadOperation { false };
bool _finishedMainThreadOperation { false };
QThread* _mainThread { nullptr };
OpenGLDisplayPlugin* _newPlugin { nullptr };
OpenGLDisplayPlugin* _activePlugin { nullptr };
QOpenGLContext* _context { nullptr };
QGLContext* _widgetContext { nullptr };
};
OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
@ -114,14 +164,16 @@ void OpenGLDisplayPlugin::activate() {
if (!presentThread) {
DependencyManager::set<PresentThread>();
presentThread = DependencyManager::get<PresentThread>();
presentThread->setObjectName("Presentation Thread");
auto widget = _container->getPrimaryWidget();
auto glContext = widget->context();
auto context = glContext->contextHandle();
glContext->moveToThread(presentThread.data());
context->moveToThread(presentThread.data());
// Move the OpenGL context to the present thread
presentThread->_context = context;
// Extra code because of the widget 'wrapper' context
presentThread->_widgetContext = widget->context();
presentThread->_widgetContext->moveToThread(presentThread.data());
presentThread->_context = presentThread->_widgetContext->contextHandle();
presentThread->_context->moveToThread(presentThread.data());
// Start execution
presentThread->start();
@ -325,3 +377,18 @@ void OpenGLDisplayPlugin::swapBuffers() {
static auto widget = _container->getPrimaryWidget();
widget->swapBuffers();
}
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
static auto presentThread = DependencyManager::get<PresentThread>();
presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent();
}
QImage OpenGLDisplayPlugin::getScreenshot() const {
QImage result;
withMainThreadContext([&] {
static auto widget = _container->getPrimaryWidget();
result = widget->grabFrameBuffer();
});
return result;
}

View file

@ -31,17 +31,20 @@ public:
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
virtual float presentRate() override;
virtual glm::uvec2 getRecommendedRenderSize() const {
virtual glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
}
virtual glm::uvec2 getRecommendedUiSize() const {
virtual glm::uvec2 getRecommendedUiSize() const override {
return getSurfaceSize();
}
virtual QImage getScreenshot() const override;
protected:
friend class PresentThread;
virtual glm::uvec2 getSurfaceSize() const = 0;
virtual glm::uvec2 getSurfacePixels() const = 0;
@ -53,6 +56,9 @@ protected:
virtual void customizeContext();
virtual void uncustomizeContext();
virtual void cleanupForSceneTexture(uint32_t sceneTexture);
void withMainThreadContext(std::function<void()> f) const;
void present();
void updateTextures();
void updateFramerate();

View file

@ -14,6 +14,7 @@
#include <QtCore/QSize>
#include <QtCore/QPoint>
class QImage;
#include <GLMHelpers.h>
#include <RegisteredMetaTypes.h>
@ -96,6 +97,9 @@ public:
return baseProjection;
}
// Fetch the most recently displayed image as a QImage
virtual QImage getScreenshot() const = 0;
// HMD specific methods
// TODO move these into another class?
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const {