diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc0feefb9a..7ad077151e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8428,6 +8428,17 @@ bool Application::takeSnapshotOperators(std::queue& snapshotOp return !snapshotOperators.empty(); } +void Application::addSecondarySnapshotOperator(const SecondarySnapshotOperator& snapshotOperator) { + std::lock_guard lock(_snapshotMutex); + _secondarySnapshotOperators.push(snapshotOperator); +} + +bool Application::takeSecondarySnapshotOperators(std::queue& snapshotOperators) { + std::lock_guard lock(_snapshotMutex); + _secondarySnapshotOperators.swap(snapshotOperators); + return !snapshotOperators.empty(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { addSnapshotOperator({ [notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) { QString path = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); @@ -8439,16 +8450,17 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa emit DependencyManager::get()->stillSnapshotTaken(path, notify); } } else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) { - // Get an animated GIF snapshot and save it - SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get()); + qApp->postLambdaEvent([path, aspectRatio] { + // Get an animated GIF snapshot and save it + SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get()); + }); } }, aspectRatio }); } void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { - postLambdaEvent([notify, filename, this] { - QString snapshotPath = DependencyManager::get()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, - TestScriptingInterface::getInstance()->getTestResultsLocation()); + addSecondarySnapshotOperator([notify, filename](const QImage& snapshot) { + QString snapshotPath = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, notify); }); diff --git a/interface/src/Application.h b/interface/src/Application.h index b2e59e97d4..0004add3b1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -345,8 +345,11 @@ public: #endif using SnapshotOperator = std::pair, float>; + using SecondarySnapshotOperator = std::function; void addSnapshotOperator(const SnapshotOperator& snapshotOperator); bool takeSnapshotOperators(std::queue& snapshotOperators); + void addSecondarySnapshotOperator(const SecondarySnapshotOperator& snapshotOperator); + bool takeSecondarySnapshotOperators(std::queue& snapshotOperators); signals: void svoImportRequested(const QString& url); @@ -794,6 +797,7 @@ private: SharedSoundPointer _sampleSound; std::mutex _snapshotMutex; std::queue _snapshotOperators; + std::queue _secondarySnapshotOperators; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; QString _autoSwitchDisplayModeSupportedHMDPluginName; diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index 1842c20e4e..09471a9570 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -245,6 +245,7 @@ void GraphicsEngine::render_performFrame() { } std::queue snapshotOperators; + std::queue secondarySnapshotOperators; if (!_programsCompiled.load()) { gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { batch.setFramebuffer(finalFramebuffer); @@ -273,12 +274,12 @@ void GraphicsEngine::render_performFrame() { renderArgs._hudOperator = displayPlugin->getHUDOperator(); renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators); + qApp->takeSecondarySnapshotOperators(secondarySnapshotOperators); renderArgs._blitFramebuffer = finalFramebuffer; render_runRenderFrame(&renderArgs); } } - qDebug() << "boop" << renderArgs._takingSnapshot << snapshotOperators.size(); auto frame = getGPUContext()->endFrame(); frame->frameIndex = _renderFrameCount; frame->framebuffer = finalFramebuffer; @@ -289,6 +290,7 @@ void GraphicsEngine::render_performFrame() { } }; frame->snapshotOperators = snapshotOperators; + frame->secondarySnapshotOperators = secondarySnapshotOperators; // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(render, "/pluginOutput"); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 60c039ce1f..94523fa6dd 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -159,47 +159,57 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, secondaryCameraRenderConfig->setOrientation(CAMERA_ORIENTATION_DOWN); _snapshotIndex = 0; + _taking360Snapshot = true; _snapshotTimer.start(SNAPSHOT_360_TIMER_INTERVAL); } void Snapshot::takeNextSnapshot() { - SecondaryCameraJobConfig* config = - static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + if (_taking360Snapshot) { + if (!_waitingOnSnapshot) { + _waitingOnSnapshot = true; + qApp->addSecondarySnapshotOperator([this](const QImage& snapshot) { + // Order is: + // 0. Down + // 1. Front + // 2. Left + // 3. Back + // 4. Right + // 5. Up + if (_snapshotIndex < 6) { + _imageArray[_snapshotIndex] = snapshot; + } - // Order is: - // 0. Down - // 1. Front - // 2. Left - // 3. Back - // 4. Right - // 5. Up - if (_snapshotIndex < 6) { - _imageArray[_snapshotIndex] = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - } + SecondaryCameraJobConfig* config = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + if (_snapshotIndex == 0) { + // Setup for Front Image capture + config->setOrientation(CAMERA_ORIENTATION_FRONT); + } else if (_snapshotIndex == 1) { + // Setup for Left Image capture + config->setOrientation(CAMERA_ORIENTATION_LEFT); + } else if (_snapshotIndex == 2) { + // Setup for Back Image capture + config->setOrientation(CAMERA_ORIENTATION_BACK); + } else if (_snapshotIndex == 3) { + // Setup for Right Image capture + config->setOrientation(CAMERA_ORIENTATION_RIGHT); + } else if (_snapshotIndex == 4) { + // Setup for Up Image capture + config->setOrientation(CAMERA_ORIENTATION_UP); + } else if (_snapshotIndex == 5) { + _taking360Snapshot = false; + } - if (_snapshotIndex == 0) { - // Setup for Front Image capture - config->setOrientation(CAMERA_ORIENTATION_FRONT); - } else if (_snapshotIndex == 1) { - // Setup for Left Image capture - config->setOrientation(CAMERA_ORIENTATION_LEFT); - } else if (_snapshotIndex == 2) { - // Setup for Back Image capture - config->setOrientation(CAMERA_ORIENTATION_BACK); - } else if (_snapshotIndex == 3) { - // Setup for Right Image capture - config->setOrientation(CAMERA_ORIENTATION_RIGHT); - } else if (_snapshotIndex == 4) { - // Setup for Up Image capture - config->setOrientation(CAMERA_ORIENTATION_UP); - } else if (_snapshotIndex > 5) { + _waitingOnSnapshot = false; + _snapshotIndex++; + }); + } + } else { _snapshotTimer.stop(); // Reset secondary camera render config - static_cast( - qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping")) - ->setCurve(1); + SecondaryCameraJobConfig* config = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1); config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height()); config->setProperty("attachedEntityId", _oldAttachedEntityId); config->setProperty("vFoV", _oldvFoV); @@ -217,8 +227,6 @@ void Snapshot::takeNextSnapshot() { QtConcurrent::run([this]() { convertToEquirectangular(); }); } } - - _snapshotIndex++; } void Snapshot::convertToCubemap() { diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 77bdfd4ac1..f13f4cd587 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -97,6 +97,8 @@ private: bool _cubemapOutputFormat; QTimer _snapshotTimer; qint16 _snapshotIndex; + bool _waitingOnSnapshot { false }; + bool _taking360Snapshot { false }; bool _oldEnabled; QVariant _oldAttachedEntityId; QVariant _oldOrientation; diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 1c5a25b68f..03ec86e342 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -27,7 +27,6 @@ QString SnapshotAnimated::snapshotAnimatedPath; QString SnapshotAnimated::snapshotStillPath; QVector SnapshotAnimated::snapshotAnimatedFrameVector; QVector SnapshotAnimated::snapshotAnimatedFrameDelayVector; -Application* SnapshotAnimated::app; float SnapshotAnimated::aspectRatio; QSharedPointer SnapshotAnimated::snapshotAnimatedDM; GifWriter SnapshotAnimated::snapshotAnimatedGifWriter; @@ -36,12 +35,11 @@ GifWriter SnapshotAnimated::snapshotAnimatedGifWriter; Setting::Handle SnapshotAnimated::alsoTakeAnimatedSnapshot("alsoTakeAnimatedSnapshot", true); Setting::Handle SnapshotAnimated::snapshotAnimatedDuration("snapshotAnimatedDuration", SNAPSNOT_ANIMATED_DURATION_SECS); -void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer dm) { +void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer dm) { // If we're not in the middle of capturing an animated snapshot... if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { SnapshotAnimated::snapshotAnimatedTimer = new QTimer(); SnapshotAnimated::aspectRatio = aspectRatio; - SnapshotAnimated::app = app; SnapshotAnimated::snapshotAnimatedDM = dm; // Define the output location of the still and animated snapshots. SnapshotAnimated::snapshotStillPath = pathStill; @@ -62,7 +60,7 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio void SnapshotAnimated::captureFrames() { if (SnapshotAnimated::snapshotAnimatedTimerRunning) { - SnapshotAnimated::app->addSnapshotOperator({ [](const QImage& snapshot) { + qApp->addSnapshotOperator({ [](const QImage& snapshot) { // Get a screenshot from the display, then scale the screenshot down, // then convert it to the image format the GIF library needs, // then save all that to the QImage named "frame" @@ -86,21 +84,21 @@ void SnapshotAnimated::captureFrames() { // If that was the last frame... if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) { SnapshotAnimated::snapshotAnimatedTimerRunning = false; - - // Notify the user that we're processing the snapshot - // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. - emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); - - // Kick off the thread that'll pack the frames into the GIF - QtConcurrent::run(processFrames); - // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE - // that the slot will not be called again in the future. - // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html - SnapshotAnimated::snapshotAnimatedTimer->stop(); - delete SnapshotAnimated::snapshotAnimatedTimer; } } }, SnapshotAnimated::aspectRatio }); + } else { + // Notify the user that we're processing the snapshot + // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. + emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); + + // Kick off the thread that'll pack the frames into the GIF + QtConcurrent::run(processFrames); + // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE + // that the slot will not be called again in the future. + // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html + SnapshotAnimated::snapshotAnimatedTimer->stop(); + delete SnapshotAnimated::snapshotAnimatedTimer; } } diff --git a/interface/src/ui/SnapshotAnimated.h b/interface/src/ui/SnapshotAnimated.h index 87ce533fc3..15734f57c8 100644 --- a/interface/src/ui/SnapshotAnimated.h +++ b/interface/src/ui/SnapshotAnimated.h @@ -42,7 +42,6 @@ private: static QVector snapshotAnimatedFrameVector; static QVector snapshotAnimatedFrameDelayVector; static QSharedPointer snapshotAnimatedDM; - static Application* app; static float aspectRatio; static GifWriter snapshotAnimatedGifWriter; @@ -51,7 +50,7 @@ private: static void processFrames(); static void clearTempVariables(); public: - static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer dm); + static void saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer dm); static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; }; static Setting::Handle alsoTakeAnimatedSnapshot; static Setting::Handle snapshotAnimatedDuration; diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 47a213cf71..fcd695bed8 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -25,12 +25,4 @@ void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { if (frame) { _gpuContext->consumeFrameUpdates(frame); } -} - -QImage NullDisplayPlugin::getScreenshot(float aspectRatio) const { - return QImage(); -} - -QImage NullDisplayPlugin::getSecondaryCameraScreenshot() const { - return QImage(); -} +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index e4ff1b8b37..7aa763bab9 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -17,8 +17,6 @@ public: glm::uvec2 getRecommendedRenderSize() const override; void submitFrame(const gpu::FramePointer& newFrame) override; - QImage getScreenshot(float aspectRatio = 0.0f) const override; - QImage getSecondaryCameraScreenshot() const override; void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override {}; void pluginUpdate() override {}; private: diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 1700ca9d54..f9b6deb846 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -732,6 +732,15 @@ void OpenGLDisplayPlugin::present() { } } + { // If we have any secondary camera snapshots this frame, handle them + PROFILE_RANGE_EX(render, "secondarySnapshotOperators", 0x00ff00ff, frameId) + while (!_currentFrame->secondarySnapshotOperators.empty()) { + auto& snapshotOperator = _currentFrame->secondarySnapshotOperators.front(); + snapshotOperator(getSecondaryCameraScreenshot()); + _currentFrame->secondarySnapshotOperators.pop(); + } + } + // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) @@ -796,7 +805,7 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { return !!_displayTexture; } -QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { +QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) { auto size = _compositeFramebuffer->getSize(); if (isHmd()) { size.x /= 2; @@ -812,24 +821,18 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { corner.x = round((size.x - bestSize.x) / 2.0f); corner.y = round((size.y - bestSize.y) / 2.0f); } - auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); - withOtherThreadContext([&] { - glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); - }); + getGLBackend()->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); return screenshot.mirrored(false, true); } -QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() const { +QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() { auto textureCache = DependencyManager::get(); auto secondaryCameraFramebuffer = textureCache->getSpectatorCameraFramebuffer(); gpu::Vec4i region(0, 0, secondaryCameraFramebuffer->getWidth(), secondaryCameraFramebuffer->getHeight()); - auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(region.z, region.w, QImage::Format_ARGB32); - withOtherThreadContext([&] { - glBackend->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot); - }); + getGLBackend()->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot); return screenshot.mirrored(false, true); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 49a38ecb4c..e56804c151 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -60,8 +60,6 @@ public: virtual bool setDisplayTexture(const QString& name) override; virtual bool onDisplayTextureReset() { return false; }; - QImage getScreenshot(float aspectRatio = 0.0f) const override; - QImage getSecondaryCameraScreenshot() const override; float presentRate() const override; @@ -185,5 +183,8 @@ protected: // be serialized through this mutex mutable Mutex _presentMutex; float _hudAlpha{ 1.0f }; + + QImage getScreenshot(float aspectRatio); + QImage getSecondaryCameraScreenshot(); }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 321bcc3fd2..874454b391 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -199,7 +199,7 @@ void HmdDisplayPlugin::internalPresent() { float newWidth = sourceSize.x - shiftLeftBy; // Experimentally adjusted the region presented in preview to avoid seeing the masked pixels and recenter the center... - static float SCALE_WIDTH = 0.9f; + static float SCALE_WIDTH = 0.8f; static float SCALE_OFFSET = 2.0f; newWidth *= SCALE_WIDTH; shiftLeftBy *= SCALE_OFFSET; diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 94c0919fdb..541b9393d3 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -43,6 +43,7 @@ namespace gpu { FramebufferRecycler framebufferRecycler; std::queue, float>> snapshotOperators; + std::queue> secondarySnapshotOperators; protected: friend class Deserializer; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 9db8fc995c..c88d76e661 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -173,10 +173,6 @@ public: return QRect(0, 0, recommendedSize.x, recommendedSize.y); } - // Fetch the most recently displayed image as a QImage - virtual QImage getScreenshot(float aspectRatio = 0.0f) const = 0; - virtual QImage getSecondaryCameraScreenshot() const = 0; - // will query the underlying hmd api to compute the most recent head pose virtual bool beginFrameRender(uint32_t frameIndex) { return true; }