Merge pull request #15405 from SamGondelman/stencil2

Case 20060: Grab correct stencil masks from display plugins
This commit is contained in:
Sam Gateau 2019-04-24 09:52:12 -07:00 committed by GitHub
commit dfce7dcaa6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 358 additions and 140 deletions

View file

@ -17,8 +17,8 @@ if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip
URL_MD5 06804ff9727b910dcd04a37c800053b5
URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.35.0.zip
URL_MD5 1e3e8b2101387af07ff9c841d0ea285e
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
LOG_DOWNLOAD 1

View file

@ -6723,6 +6723,11 @@ void Application::updateRenderArgs(float deltaTime) {
}
}
appRenderArgs._renderArgs._stencilMaskMode = getActiveDisplayPlugin()->getStencilMaskMode();
if (appRenderArgs._renderArgs._stencilMaskMode == StencilMaskMode::MESH) {
appRenderArgs._renderArgs._stencilMaskOperator = getActiveDisplayPlugin()->getStencilMaskMeshOperator();
}
{
QMutexLocker viewLocker(&_viewMutex);
_myCamera.loadViewFrustum(_displayViewFrustum);
@ -8410,11 +8415,23 @@ void Application::loadAvatarBrowser() const {
DependencyManager::get<HMDScriptingInterface>()->openTablet();
}
void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) {
std::lock_guard<std::mutex> lock(_snapshotMutex);
_snapshotOperators.push(snapshotOperator);
_hasPrimarySnapshot = _hasPrimarySnapshot || std::get<2>(snapshotOperator);
}
bool Application::takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOperators) {
std::lock_guard<std::mutex> lock(_snapshotMutex);
bool hasPrimarySnapshot = _hasPrimarySnapshot;
_hasPrimarySnapshot = false;
_snapshotOperators.swap(snapshotOperators);
return hasPrimarySnapshot;
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
// Get a screenshot and save it
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename,
TestScriptingInterface::getInstance()->getTestResultsLocation());
addSnapshotOperator(std::make_tuple([notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) {
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
// If we're not doing an animated snapshot as well...
if (!includeAnimated) {
@ -8423,19 +8440,20 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
}
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
// Get an animated GIF snapshot and save it
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
qApp->postLambdaEvent([path, aspectRatio] {
// Get an animated GIF snapshot and save it
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get<WindowScriptingInterface>());
});
}
});
}, aspectRatio, true));
}
void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {
postLambdaEvent([notify, filename, this] {
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename,
TestScriptingInterface::getInstance()->getTestResultsLocation());
addSnapshotOperator(std::make_tuple([notify, filename](const QImage& snapshot) {
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify);
});
}, 0.0f, false));
}
void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {

View file

@ -345,6 +345,10 @@ public:
void toggleAwayMode();
#endif
using SnapshotOperator = std::tuple<std::function<void(const QImage&)>, float, bool>;
void addSnapshotOperator(const SnapshotOperator& snapshotOperator);
bool takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOperators);
signals:
void svoImportRequested(const QString& url);
@ -789,6 +793,9 @@ private:
AudioInjectorPointer _snapshotSoundInjector;
SharedSoundPointer _snapshotSound;
SharedSoundPointer _sampleSound;
std::mutex _snapshotMutex;
std::queue<SnapshotOperator> _snapshotOperators;
bool _hasPrimarySnapshot { false };
DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin;
QString _autoSwitchDisplayModeSupportedHMDPluginName;

View file

@ -152,10 +152,12 @@ public:
_cachedArgsPointer->_viewport = args->_viewport;
_cachedArgsPointer->_displayMode = args->_displayMode;
_cachedArgsPointer->_renderMode = args->_renderMode;
_cachedArgsPointer->_stencilMaskMode = args->_stencilMaskMode;
args->_blitFramebuffer = destFramebuffer;
args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight());
args->_displayMode = RenderArgs::MONO;
args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE;
args->_stencilMaskMode = StencilMaskMode::NONE;
gpu::doInBatch("SecondaryCameraJob::run", args->_context, [&](gpu::Batch& batch) {
batch.disableContextStereo();
@ -255,10 +257,11 @@ public:
void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) {
auto args = renderContext->args;
if (cachedArgs) {
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
args->_viewport = cachedArgs->_viewport;
args->_displayMode = cachedArgs->_displayMode;
args->_renderMode = cachedArgs->_renderMode;
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
args->_viewport = cachedArgs->_viewport;
args->_displayMode = cachedArgs->_displayMode;
args->_renderMode = cachedArgs->_renderMode;
args->_stencilMaskMode = cachedArgs->_stencilMaskMode;
}
args->popViewFrustum();

View file

@ -244,6 +244,7 @@ void GraphicsEngine::render_performFrame() {
finalFramebuffer = framebufferCache->getFramebuffer();
}
std::queue<Application::SnapshotOperator> snapshotOperators;
if (!_programsCompiled.load()) {
gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) {
batch.setFramebuffer(finalFramebuffer);
@ -271,6 +272,7 @@ void GraphicsEngine::render_performFrame() {
PROFILE_RANGE(render, "/runRenderFrame");
renderArgs._hudOperator = displayPlugin->getHUDOperator();
renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture();
renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators);
renderArgs._blitFramebuffer = finalFramebuffer;
render_runRenderFrame(&renderArgs);
}
@ -285,6 +287,7 @@ void GraphicsEngine::render_performFrame() {
frameBufferCache->releaseFramebuffer(framebuffer);
}
};
frame->snapshotOperators = snapshotOperators;
// deliver final scene rendering commands to the display plugin
{
PROFILE_RANGE(render, "/pluginOutput");

View file

@ -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<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
if (_taking360Snapshot) {
if (!_waitingOnSnapshot) {
_waitingOnSnapshot = true;
qApp->addSnapshotOperator(std::make_tuple([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<SecondaryCameraJobConfig*>(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++;
}, 0.0f, false));
}
} else {
_snapshotTimer.stop();
// Reset secondary camera render config
static_cast<ToneMappingConfig*>(
qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
->setCurve(1);
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
static_cast<ToneMappingConfig*>(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() {

View file

@ -97,6 +97,8 @@ private:
bool _cubemapOutputFormat;
QTimer _snapshotTimer;
qint16 _snapshotIndex;
bool _waitingOnSnapshot { false };
bool _taking360Snapshot { false };
bool _oldEnabled;
QVariant _oldAttachedEntityId;
QVariant _oldOrientation;

View file

@ -27,7 +27,6 @@ QString SnapshotAnimated::snapshotAnimatedPath;
QString SnapshotAnimated::snapshotStillPath;
QVector<QImage> SnapshotAnimated::snapshotAnimatedFrameVector;
QVector<qint64> SnapshotAnimated::snapshotAnimatedFrameDelayVector;
Application* SnapshotAnimated::app;
float SnapshotAnimated::aspectRatio;
QSharedPointer<WindowScriptingInterface> SnapshotAnimated::snapshotAnimatedDM;
GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
@ -36,12 +35,11 @@ GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
Setting::Handle<bool> SnapshotAnimated::alsoTakeAnimatedSnapshot("alsoTakeAnimatedSnapshot", true);
Setting::Handle<float> SnapshotAnimated::snapshotAnimatedDuration("snapshotAnimatedDuration", SNAPSNOT_ANIMATED_DURATION_SECS);
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm) {
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer<WindowScriptingInterface> 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,44 +60,45 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio
void SnapshotAnimated::captureFrames() {
if (SnapshotAnimated::snapshotAnimatedTimerRunning) {
// 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"
QImage frame(SnapshotAnimated::app->getActiveDisplayPlugin()->getScreenshot(SnapshotAnimated::aspectRatio));
frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
qApp->addSnapshotOperator(std::make_tuple([](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"
QImage frame = snapshot.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
// If that was the first frame...
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
// Record the current frame timestamp
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
// Record the first frame timestamp
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
// If this is an intermediate or the final frame...
} else {
// Push the current frame delay onto the vector
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
// Record the current frame timestamp
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
// If that was the first frame...
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
// Record the current frame timestamp
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
// Record the first frame timestamp
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
// If this is an intermediate or the final frame...
} else {
// Push the current frame delay onto the vector
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
// Record the current frame timestamp
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
// 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;
// If that was the last frame...
if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) {
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
}
}
}
}, SnapshotAnimated::aspectRatio, true));
} 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;
}
}

View file

@ -42,7 +42,6 @@ private:
static QVector<QImage> snapshotAnimatedFrameVector;
static QVector<qint64> snapshotAnimatedFrameDelayVector;
static QSharedPointer<WindowScriptingInterface> 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<WindowScriptingInterface> dm);
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer<WindowScriptingInterface> dm);
static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; };
static Setting::Handle<bool> alsoTakeAnimatedSnapshot;
static Setting::Handle<float> snapshotAnimatedDuration;

View file

@ -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();
}
}

View file

@ -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:

View file

@ -721,6 +721,19 @@ void OpenGLDisplayPlugin::present() {
compositeLayers();
}
{ // If we have any snapshots this frame, handle them
PROFILE_RANGE_EX(render, "snapshotOperators", 0xffff00ff, frameId)
while (!_currentFrame->snapshotOperators.empty()) {
auto& snapshotOperator = _currentFrame->snapshotOperators.front();
if (std::get<2>(snapshotOperator)) {
std::get<0>(snapshotOperator)(getScreenshot(std::get<1>(snapshotOperator)));
} else {
std::get<0>(snapshotOperator)(getSecondaryCameraScreenshot());
}
_currentFrame->snapshotOperators.pop();
}
}
// Take the composite framebuffer and send it to the output device
{
PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
@ -785,7 +798,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;
@ -801,24 +814,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<OpenGLDisplayPlugin&>(*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<TextureCache>();
auto secondaryCameraFramebuffer = textureCache->getSpectatorCameraFramebuffer();
gpu::Vec4i region(0, 0, secondaryCameraFramebuffer->getWidth(), secondaryCameraFramebuffer->getHeight());
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*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);
}

View file

@ -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();
};

View file

@ -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;

View file

@ -48,6 +48,8 @@ public:
void pluginUpdate() override {};
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; }
signals:
void hmdMountedChanged();
void hmdVisibleChanged(bool visible);

View file

@ -9,6 +9,7 @@
#define hifi_gpu_Frame_h
#include <functional>
#include <queue>
#include "Forward.h"
#include "Batch.h"
@ -41,6 +42,8 @@ namespace gpu {
/// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE
FramebufferRecycler framebufferRecycler;
std::queue<std::tuple<std::function<void(const QImage&)>, float, bool>> snapshotOperators;
protected:
friend class Deserializer;

View file

@ -27,6 +27,7 @@
#include <SimpleMovingAverage.h>
#include <gpu/Forward.h>
#include "Plugin.h"
#include "StencilMaskMode.h"
class QOpenGLFramebufferObject;
@ -172,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; }
@ -221,6 +218,10 @@ public:
// for updating plugin-related commands. Mimics the input plugin.
virtual void pluginUpdate() = 0;
virtual StencilMaskMode getStencilMaskMode() const { return StencilMaskMode::NONE; }
using StencilMaskMeshOperator = std::function<void(gpu::Batch&)>;
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() { return nullptr; }
signals:
void recommendedFramebufferSizeChanged(const QSize& size);
void resetSensorsRequested();

View file

@ -19,7 +19,6 @@ using namespace render;
void PrepareStencil::configure(const Config& config) {
_maskMode = config.maskMode;
_forceDraw = config.forceDraw;
}
graphics::MeshPointer PrepareStencil::getMesh() {
@ -43,6 +42,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() {
auto state = std::make_shared<gpu::State>();
drawMask(*state);
state->setColorWriteMask(gpu::State::WRITE_NONE);
state->setCullMode(gpu::State::CullMode::CULL_NONE);
_meshStencilPipeline = gpu::Pipeline::create(program, state);
}
@ -64,8 +64,28 @@ gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() {
void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
RenderArgs* args = renderContext->args;
// Only draw the stencil mask if in HMD mode or not forced.
if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) {
if (args->_takingSnapshot) {
return;
}
StencilMaskMode maskMode = _maskMode;
std::function<void(gpu::Batch&)> maskOperator = [this](gpu::Batch& batch) {
auto mesh = getMesh();
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
};
if (maskMode == StencilMaskMode::NONE) {
maskMode = args->_stencilMaskMode;
maskOperator = args->_stencilMaskOperator;
}
if (maskMode == StencilMaskMode::NONE || (maskMode == StencilMaskMode::MESH && !maskOperator)) {
return;
}
@ -74,20 +94,12 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F
batch.setViewportTransform(args->_viewport);
if (_maskMode < 0) {
batch.setPipeline(getMeshStencilPipeline());
auto mesh = getMesh();
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
} else {
if (maskMode == StencilMaskMode::PAINT) {
batch.setPipeline(getPaintStencilPipeline());
batch.draw(gpu::TRIANGLE_STRIP, 4);
} else if (maskMode == StencilMaskMode::MESH) {
batch.setPipeline(getMeshStencilPipeline());
maskOperator(batch);
}
});
}

View file

@ -15,17 +15,19 @@
#include <render/Engine.h>
#include <gpu/Pipeline.h>
#include <graphics/Geometry.h>
#include <StencilMaskMode.h>
class PrepareStencilConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty)
Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty)
Q_PROPERTY(StencilMaskMode maskMode MEMBER maskMode NOTIFY dirty)
public:
PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {}
int maskMode { 0 };
bool forceDraw { false };
// -1 -> don't force drawing (fallback to render args mode)
// 0 -> force draw without mesh
// 1 -> force draw with mesh
StencilMaskMode maskMode { StencilMaskMode::NONE };
signals:
void dirty();
@ -66,8 +68,7 @@ private:
graphics::MeshPointer _mesh;
graphics::MeshPointer getMesh();
int _maskMode { 0 };
bool _forceDraw { false };
StencilMaskMode _maskMode { StencilMaskMode::NONE };
};

View file

@ -16,6 +16,7 @@
#include <GLMHelpers.h>
#include <ViewFrustum.h>
#include <StencilMaskMode.h>
#include <gpu/Forward.h>
#include "Forward.h"
@ -133,6 +134,10 @@ namespace render {
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator;
gpu::TexturePointer _hudTexture;
bool _takingSnapshot { false };
StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE };
std::function<void(gpu::Batch&)> _stencilMaskOperator;
};
}

View file

@ -40,6 +40,7 @@ int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
int socketErrorMetaTypeId = qRegisterMetaType<QAbstractSocket::SocketError>();
int voidLambdaType = qRegisterMetaType<std::function<void()>>();
int variantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
int stencilModeMetaTypeId = qRegisterMetaType<StencilMaskMode>();
void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue);
@ -64,6 +65,8 @@ void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue);
qScriptRegisterMetaType(engine, stencilMaskModeToScriptValue, stencilMaskModeFromScriptValue);
}
QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) {
@ -1358,4 +1361,12 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe
}
return toReturn;
}
QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode) {
return engine->newVariant((int)stencilMode);
}
void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode) {
stencilMode = StencilMaskMode(object.toVariant().toInt());
}

View file

@ -25,6 +25,7 @@
#include "shared/Bilateral.h"
#include "Transform.h"
#include "PhysicsCollisionGroups.h"
#include "StencilMaskMode.h"
class QColor;
class QUrl;
@ -733,5 +734,8 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>
QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures);
Q_DECLARE_METATYPE(StencilMaskMode)
QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode);
void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode);
#endif // hifi_RegisteredMetaTypes_h

View file

@ -0,0 +1,18 @@
//
// Created by Sam Gondelman on 3/26/19.
// Copyright 2019 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
//
#ifndef hifi_StencilMaskMode_h
#define hifi_StencilMaskMode_h
enum class StencilMaskMode {
NONE = -1, // for legacy reasons, this is -1
PAINT = 0,
MESH = 1
};
#endif // hifi_StencilMaskMode_h

View file

@ -18,7 +18,7 @@ if (WIN32 AND (NOT USE_GLES))
link_hifi_libraries(
shared task gl shaders gpu ${PLATFORM_GL_BACKEND} controllers ui qml
plugins ui-plugins display-plugins input-plugins
audio-client networking render-utils
audio-client networking render-utils graphics
${PLATFORM_GL_BACKEND}
)
include_hifi_library_headers(octree)

View file

@ -227,3 +227,66 @@ QVector<glm::vec3> OculusBaseDisplayPlugin::getSensorPositions() {
return result;
}
DisplayPlugin::StencilMaskMeshOperator OculusBaseDisplayPlugin::getStencilMaskMeshOperator() {
if (_session) {
if (!_stencilMeshesInitialized) {
_stencilMeshesInitialized = true;
ovr::for_each_eye([&](ovrEyeType eye) {
ovrFovStencilDesc stencilDesc = {
ovrFovStencil_HiddenArea, 0, eye,
_eyeRenderDescs[eye].Fov, _eyeRenderDescs[eye].HmdToEyePose.Orientation
};
// First we get the size of the buffer we need
ovrFovStencilMeshBuffer buffer = { 0, 0, nullptr, 0, 0, nullptr };
ovrResult result = ovr_GetFovStencil(_session, &stencilDesc, &buffer);
if (!OVR_SUCCESS(result)) {
_stencilMeshesInitialized = false;
return;
}
std::vector<ovrVector2f> ovrVertices(buffer.UsedVertexCount);
std::vector<uint16_t> ovrIndices(buffer.UsedIndexCount);
// Now we populate the actual buffer
buffer = { (int)ovrVertices.size(), 0, ovrVertices.data(), (int)ovrIndices.size(), 0, ovrIndices.data() };
result = ovr_GetFovStencil(_session, &stencilDesc, &buffer);
if (!OVR_SUCCESS(result)) {
_stencilMeshesInitialized = false;
return;
}
std::vector<glm::vec3> vertices;
vertices.reserve(ovrVertices.size());
for (auto& ovrVertex : ovrVertices) {
// We need the vertices in clip space
vertices.emplace_back(ovrVertex.x - (1.0f - (float)eye), 2.0f * ovrVertex.y - 1.0f, 0.0f);
}
std::vector<uint32_t> indices;
indices.reserve(ovrIndices.size());
for (auto& ovrIndex : ovrIndices) {
indices.push_back(ovrIndex);
}
_stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data());
});
}
if (_stencilMeshesInitialized) {
return [&](gpu::Batch& batch) {
for (auto& mesh : _stencilMeshes) {
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
}
};
}
}
return nullptr;
}

View file

@ -16,6 +16,8 @@
#define OVRPL_DISABLED
#include <OVR_Platform.h>
#include <graphics/Geometry.h>
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
@ -34,6 +36,9 @@ public:
QRectF getPlayAreaRect() override;
QVector<glm::vec3> getSensorPositions() override;
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; }
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override;
protected:
void customizeContext() override;
void uncustomizeContext() override;
@ -52,4 +57,7 @@ protected:
// ovrLayerEyeFovDepth _depthLayer;
bool _hmdMounted { false };
bool _visible { true };
std::array<graphics::MeshPointer, 2> _stencilMeshes;
bool _stencilMeshesInitialized { false };
};

View file

@ -784,3 +784,48 @@ QRectF OpenVrDisplayPlugin::getPlayAreaRect() {
return QRectF(center.x, center.y, dimensions.x, dimensions.y);
}
DisplayPlugin::StencilMaskMeshOperator OpenVrDisplayPlugin::getStencilMaskMeshOperator() {
if (_system) {
if (!_stencilMeshesInitialized) {
_stencilMeshesInitialized = true;
for (auto eye : VR_EYES) {
vr::HiddenAreaMesh_t stencilMesh = _system->GetHiddenAreaMesh(eye);
if (stencilMesh.pVertexData && stencilMesh.unTriangleCount > 0) {
std::vector<glm::vec3> vertices;
std::vector<uint32_t> indices;
const int NUM_INDICES_PER_TRIANGLE = 3;
int numIndices = stencilMesh.unTriangleCount * NUM_INDICES_PER_TRIANGLE;
vertices.reserve(numIndices);
indices.reserve(numIndices);
for (int i = 0; i < numIndices; i++) {
vr::HmdVector2_t vertex2D = stencilMesh.pVertexData[i];
// We need the vertices in clip space
vertices.emplace_back(vertex2D.v[0] - (1.0f - (float)eye), 2.0f * vertex2D.v[1] - 1.0f, 0.0f);
indices.push_back(i);
}
_stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data());
} else {
_stencilMeshesInitialized = false;
}
}
}
if (_stencilMeshesInitialized) {
return [&](gpu::Batch& batch) {
for (auto& mesh : _stencilMeshes) {
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
}
};
}
}
return nullptr;
}

View file

@ -13,6 +13,8 @@
#include <display-plugins/hmd/HmdDisplayPlugin.h>
#include <graphics/Geometry.h>
const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only.
namespace gl {
@ -67,6 +69,9 @@ public:
QRectF getPlayAreaRect() override;
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; }
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override;
protected:
bool internalActivate() override;
void internalDeactivate() override;
@ -94,4 +99,7 @@ private:
bool _asyncReprojectionActive { false };
bool _hmdMounted { false };
std::array<graphics::MeshPointer, 2> _stencilMeshes;
bool _stencilMeshesInitialized { false };
};