diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html index 965f86b0a1..0dc08e6e31 100644 --- a/domain-server/resources/web/header.html +++ b/domain-server/resources/web/header.html @@ -10,7 +10,6 @@ - + + +
diff --git a/domain-server/resources/web/js/domain-server.js b/domain-server/resources/web/js/domain-server.js index 3f78d8f466..88ab7b1470 100644 --- a/domain-server/resources/web/js/domain-server.js +++ b/domain-server/resources/web/js/domain-server.js @@ -1,3 +1,28 @@ +function showRestartModal() { + $('#restart-modal').modal({ + backdrop: 'static', + keyboard: false + }); + + var secondsElapsed = 0; + var numberOfSecondsToWait = 3; + + var refreshSpan = $('span#refresh-time') + refreshSpan.html(numberOfSecondsToWait + " seconds"); + + // call ourselves every 1 second to countdown + var refreshCountdown = setInterval(function(){ + secondsElapsed++; + secondsLeft = numberOfSecondsToWait - secondsElapsed + refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds")) + + if (secondsElapsed == numberOfSecondsToWait) { + location.reload(true); + clearInterval(refreshCountdown); + } + }, 1000); +} + $(document).ready(function(){ var url = window.location; // Will only work if string in href matches with location @@ -7,4 +32,10 @@ $(document).ready(function(){ $('ul.nav a').filter(function() { return this.href == url; }).parent().addClass('active'); + + $('body').on('click', '#restart-server', function(e){ + $.get("/restart"); + showRestartModal(); + return false; + }); }); \ No newline at end of file diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 9d38539b42..13f5668010 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -81,19 +81,6 @@
- - diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index dd577701a5..0057df721a 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1680,31 +1680,6 @@ function updateDataChangedForSiblingRows(row, forceTrue) { }) } -function showRestartModal() { - $('#restart-modal').modal({ - backdrop: 'static', - keyboard: false - }); - - var secondsElapsed = 0; - var numberOfSecondsToWait = 3; - - var refreshSpan = $('span#refresh-time') - refreshSpan.html(numberOfSecondsToWait + " seconds"); - - // call ourselves every 1 second to countdown - var refreshCountdown = setInterval(function(){ - secondsElapsed++; - secondsLeft = numberOfSecondsToWait - secondsElapsed - refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds")) - - if (secondsElapsed == numberOfSecondsToWait) { - location.reload(true); - clearInterval(refreshCountdown); - } - }, 1000); -} - function cleanupFormValues(node) { if (node.type && node.type === 'checkbox') { return { name: node.name, value: node.checked ? true : false }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d637a20454..0c1ebbf189 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1650,6 +1650,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_NODES = "/nodes"; const QString URI_SETTINGS = "/settings"; const QString URI_ENTITY_FILE_UPLOAD = "/content/upload"; + const QString URI_RESTART = "/restart"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -1804,6 +1805,10 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // send the response connection->respond(HTTPConnection::StatusCode200, nodesDocument.toJson(), qPrintable(JSON_MIME_TYPE)); + return true; + } else if (url.path() == URI_RESTART) { + connection->respond(HTTPConnection::StatusCode200); + restart(); return true; } else { // check if this is for json stats for a node diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index a30aba2a6b..929752f925 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -18,7 +18,7 @@ import QtQuick.Layouts 1.3 import "../../styles-uit" import "../../controls-uit" as HifiControls import "../../windows" -import "./" as Audio +import "./" as AudioControls Rectangle { id: root; @@ -57,7 +57,7 @@ Rectangle { x: 16; // padding does not work spacing: 16; - Audio.CheckBox { + AudioControls.CheckBox { text: qsTr("Mute microphone"); checked: Audio.muted; onClicked: { @@ -65,7 +65,7 @@ Rectangle { checked = Qt.binding(function() { return Audio.muted; }); // restore binding } } - Audio.CheckBox { + AudioControls.CheckBox { text: qsTr("Enable noise reduction"); checked: Audio.noiseReduction; onClicked: { @@ -73,7 +73,7 @@ Rectangle { checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding } } - Audio.CheckBox { + AudioControls.CheckBox { text: qsTr("Show audio level meter"); checked: AvatarInputs.showAudioTools; onClicked: { @@ -110,7 +110,7 @@ Rectangle { delegate: Item { width: parent.width; height: 36; - Audio.CheckBox { + AudioControls.CheckBox { text: display; checked: selected; onClicked: { @@ -148,7 +148,7 @@ Rectangle { delegate: Item { width: parent.width; height: 36; - Audio.CheckBox { + AudioControls.CheckBox { text: display; checked: selected; onClicked: { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c2d4360639..a4d28b6055 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -115,6 +115,7 @@ #include #include #include +#include #include #include #include @@ -1899,6 +1900,7 @@ void Application::initializeGL() { render::CullFunctor cullFunctor = LODManager::shouldRender; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); + _renderEngine->addJob("SecondaryCameraFrame", cullFunctor); _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp new file mode 100644 index 0000000000..f6ee8caa61 --- /dev/null +++ b/interface/src/SecondaryCamera.cpp @@ -0,0 +1,125 @@ +// +// SecondaryCamera.cpp +// interface/src +// +// Created by Samuel Gateau, Howard Stearns, and Zach Fox on 2017-06-08. +// Copyright 2013 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 +// + +#include "SecondaryCamera.h" +#include +#include + +using RenderArgsPointer = std::shared_ptr; + +void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { + + task.addJob("RenderShadowTask", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor); + assert(items.canCast()); + if (!isDeferred) { + task.addJob("Forward", items); + } else { + task.addJob("RenderDeferredTask", items); + } +} + +void SecondaryCameraRenderTaskConfig::resetSize(int width, int height) { // FIXME: Add an arg here for "destinationFramebuffer" + bool wasEnabled = isEnabled(); + setEnabled(false); + auto textureCache = DependencyManager::get(); + textureCache->resetSpectatorCameraFramebuffer(width, height); // FIXME: Call the correct reset function based on the "destinationFramebuffer" arg + setEnabled(wasEnabled); +} + +void SecondaryCameraRenderTaskConfig::resetSizeSpectatorCamera(int width, int height) { // Carefully adjust the framebuffer / texture. + resetSize(width, height); +} + +class BeginSecondaryCameraFrame { // Changes renderContext for our framebuffer and and view. + glm::vec3 _position{}; + glm::quat _orientation{}; + float _vFoV{}; + float _nearClipPlaneDistance{}; + float _farClipPlaneDistance{}; +public: + using Config = BeginSecondaryCameraFrameConfig; + using JobModel = render::Job::ModelO; + BeginSecondaryCameraFrame() { + _cachedArgsPointer = std::make_shared(_cachedArgs); + } + + void configure(const Config& config) { + if (config.enabled || config.alwaysEnabled) { + _position = config.position; + _orientation = config.orientation; + _vFoV = config.vFoV; + _nearClipPlaneDistance = config.nearClipPlaneDistance; + _farClipPlaneDistance = config.farClipPlaneDistance; + } + } + + void run(const render::RenderContextPointer& renderContext, RenderArgsPointer& cachedArgs) { + auto args = renderContext->args; + auto textureCache = DependencyManager::get(); + gpu::FramebufferPointer destFramebuffer; + destFramebuffer = textureCache->getSpectatorCameraFramebuffer(); // FIXME: Change the destination based on some unimplemented config var + if (destFramebuffer) { + _cachedArgsPointer->_blitFramebuffer = args->_blitFramebuffer; + _cachedArgsPointer->_viewport = args->_viewport; + _cachedArgsPointer->_displayMode = args->_displayMode; + _cachedArgsPointer->_renderMode = args->_renderMode; + 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; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.disableContextStereo(); + }); + + auto srcViewFrustum = args->getViewFrustum(); + srcViewFrustum.setPosition(_position); + srcViewFrustum.setOrientation(_orientation); + srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV), ((float)args->_viewport.z / (float)args->_viewport.w), _nearClipPlaneDistance, _farClipPlaneDistance)); + // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera, + // which is not what we want here. + srcViewFrustum.calculate(); + args->pushViewFrustum(srcViewFrustum); + cachedArgs = _cachedArgsPointer; + } + } + +protected: + RenderArgs _cachedArgs; + RenderArgsPointer _cachedArgsPointer; +}; + +class EndSecondaryCameraFrame { // Restores renderContext. +public: + using JobModel = render::Job::ModelI; + + void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) { + auto args = renderContext->args; + args->_blitFramebuffer = cachedArgs->_blitFramebuffer; + args->_viewport = cachedArgs->_viewport; + args->popViewFrustum(); + args->_displayMode = cachedArgs->_displayMode; + args->_renderMode = cachedArgs->_renderMode; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.restoreContextStereo(); + }); + } +}; + +void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) { + const auto cachedArg = task.addJob("BeginSecondaryCamera"); + const auto items = task.addJob("FetchCullSort", cullFunctor); + assert(items.canCast()); + task.addJob("RenderDeferredTask", items); + task.addJob("EndSecondaryCamera", cachedArg); +} \ No newline at end of file diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h new file mode 100644 index 0000000000..5ad19c9614 --- /dev/null +++ b/interface/src/SecondaryCamera.h @@ -0,0 +1,70 @@ +// +// SecondaryCamera.h +// interface/src +// +// Created by Samuel Gateau, Howard Stearns, and Zach Fox on 2017-06-08. +// Copyright 2013 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 +// + +#pragma once +#ifndef hifi_SecondaryCamera_h +#define hifi_SecondaryCamera_h + +#include +#include +#include +#include + + +class MainRenderTask { +public: + using JobModel = render::Task::Model; + + MainRenderTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true); +}; + +class BeginSecondaryCameraFrameConfig : public render::Task::Config { // Exposes secondary camera parameters to JavaScript. + Q_OBJECT + Q_PROPERTY(glm::vec3 position MEMBER position NOTIFY dirty) // of viewpoint to render from + Q_PROPERTY(glm::quat orientation MEMBER orientation NOTIFY dirty) // of viewpoint to render from + Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees. + Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters. + Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters. +public: + glm::vec3 position{}; + glm::quat orientation{}; + float vFoV{ 45.0f }; + float nearClipPlaneDistance{ 0.1f }; + float farClipPlaneDistance{ 100.0f }; + BeginSecondaryCameraFrameConfig() : render::Task::Config(false) {} +signals: + void dirty(); +}; + +class SecondaryCameraRenderTaskConfig : public render::Task::Config { + Q_OBJECT +public: + SecondaryCameraRenderTaskConfig() : render::Task::Config(false) {} +private: + void resetSize(int width, int height); +signals: + void dirty(); +public slots: + void resetSizeSpectatorCamera(int width, int height); +}; + +class SecondaryCameraRenderTask { +public: + using Config = SecondaryCameraRenderTaskConfig; + using JobModel = render::Task::Model; + SecondaryCameraRenderTask() {} + void configure(const Config& config) {} + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); +}; + +#endif diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1adcfbd345..18bee773b0 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1881,15 +1881,14 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f; -bool MyAvatar::cameraInsideHead() const { - const glm::vec3 cameraPosition = qApp->getCamera().getPosition(); +bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; - bool insideHead = cameraInsideHead(); + bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); return !defaultMode || !firstPerson || !insideHead; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f61f24fb11..752b89bef6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -615,7 +615,7 @@ private: float scale = 1.0f, bool isSoft = false, bool allowDuplicates = false, bool useSaved = true) override; - bool cameraInsideHead() const; + bool cameraInsideHead(const glm::vec3& cameraPosition) const; void updateEyeContactTarget(float deltaTime); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 1e14c24da3..38f467f22b 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -284,6 +284,11 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { QApplication::clipboard()->setText(text); } + +bool WindowScriptingInterface::setDisplayTexture(const QString& name) { + return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false. +} + void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { qApp->takeSnapshot(notify, includeAnimated, aspectRatio); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 2b1e48d918..4fb4829636 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -62,6 +62,7 @@ public slots: void displayAnnouncement(const QString& message); void shareSnapshot(const QString& path, const QUrl& href = QUrl("")); bool isPhysicsEnabled(); + bool setDisplayTexture(const QString& name); int openMessageBox(QString title, QString text, int buttons, int defaultButton); void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index d78287a0e7..a0a348388e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -288,6 +288,13 @@ void Avatar::updateAvatarEntities() { properties.setScript(noScript); } + // When grabbing avatar entities, they are parented to the joint moving them, then when un-grabbed + // they go back to the default parent (null uuid). When un-gripped, others saw the entity disappear. + // The thinking here is the local position was noticed as changing, but not the parentID (since it is now + // back to the default), and the entity flew off somewhere. Marking all changed definitely fixes this, + // and seems safe (per Seth). + properties.markAllChanged(); + // try to build the entity EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); bool success = true; @@ -1067,15 +1074,15 @@ void Avatar::setModelURLFinished(bool success) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { - qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL + qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL << "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts."; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } else { - qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL - << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() + qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL + << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() << "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS; } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index bfd158ffb5..67bbb452ca 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -496,6 +496,17 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { _newFrameQueue.push(newFrame); }); } +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(scissor); + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, texture); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); +} void OpenGLDisplayPlugin::updateFrameData() { PROFILE_RANGE(render, __FUNCTION__) @@ -605,14 +616,11 @@ void OpenGLDisplayPlugin::compositeLayers() { void OpenGLDisplayPlugin::internalPresent() { render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); + // Note: _displayTexture must currently be the same size as the display. + uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); + auto viewport = ivec4(uvec2(0), dims); + renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); + }); swapBuffers(); _presentRate.increment(); } @@ -694,6 +702,22 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { _container->makeRenderingContextCurrent(); } +bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { + // Note: it is the caller's responsibility to keep the network texture in cache. + if (name.isEmpty()) { + _displayTexture.reset(); + onDisplayTextureReset(); + return true; + } + auto textureCache = DependencyManager::get(); + auto displayNetworkTexture = textureCache->getTexture(name); + if (!displayNetworkTexture) { + return false; + } + _displayTexture = displayNetworkTexture->getGPUTexture(); + return !!_displayTexture; +} + QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto size = _compositeFramebuffer->getSize(); if (isHmd()) { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 10a7558398..7e7889ff47 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -57,6 +57,8 @@ public: return getSurfaceSize(); } + virtual bool setDisplayTexture(const QString& name) override; + virtual bool onDisplayTextureReset() { return false; }; QImage getScreenshot(float aspectRatio = 0.0f) const override; float presentRate() const override; @@ -109,6 +111,7 @@ protected: // Plugin specific functionality to send the composed scene to the output window or device virtual void internalPresent(); + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor); virtual void updateFrameData(); void withMainThreadContext(std::function f) const; @@ -134,6 +137,7 @@ protected: gpu::PipelinePointer _simplePipeline; gpu::PipelinePointer _presentPipeline; gpu::PipelinePointer _cursorPipeline; + gpu::TexturePointer _displayTexture{}; float _compositeOverlayAlpha { 1.0f }; struct CursorData { diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 08c8d4f754..ea91890f33 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include "../Logging.h" @@ -211,7 +212,15 @@ void HmdDisplayPlugin::internalPresent() { // Composite together the scene, overlay and mouse cursor hmdPresent(); - if (!_disablePreview) { + if (_displayTexture) { + // Note: _displayTexture must currently be the same size as the display. + uvec2 dims = uvec2(_displayTexture->getDimensions()); + auto viewport = ivec4(uvec2(0), dims); + render([&](gpu::Batch& batch) { + renderFromTexture(batch, _displayTexture, viewport, viewport); + }); + swapBuffers(); + } else if (!_disablePreview) { // screen preview mirroring auto sourceSize = _renderTargetSize; if (_monoPreview) { @@ -278,16 +287,7 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } - - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(scissor); // was viewport - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); + renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor); }); swapBuffers(); } else if (_clearPreviewFlag) { @@ -316,15 +316,7 @@ void HmdDisplayPlugin::internalPresent() { auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(viewport); - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _previewTexture); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); + renderFromTexture(batch, _previewTexture, viewport, viewport); }); _clearPreviewFlag = false; swapBuffers(); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index aaa6e347e0..055328ee21 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -46,6 +46,8 @@ public: float stutterRate() const override; + virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; }; + protected: virtual void hmdPresent() = 0; virtual bool isHmdMounted() const = 0; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index ed81b502fc..791130ef6e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -115,6 +115,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_resetStages), + (&::gpu::gl::GLBackend::do_disableContextStereo), + (&::gpu::gl::GLBackend::do_restoreContextStereo), + (&::gpu::gl::GLBackend::do_runLambda), (&::gpu::gl::GLBackend::do_startNamedCall), @@ -224,6 +227,14 @@ void GLBackend::renderPassTransfer(const Batch& batch) { _transform.preUpdate(_commandIndex, _stereo); break; + case Batch::COMMAND_disableContextStereo: + _stereo._contextDisable = true; + break; + + case Batch::COMMAND_restoreContextStereo: + _stereo._contextDisable = false; + break; + case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: { @@ -308,16 +319,16 @@ void GLBackend::render(const Batch& batch) { } #ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo._enable) { + if (_stereo.isStereo()) { glEnable(GL_CLIP_DISTANCE0); } #endif { - PROFILE_RANGE(render_gpu_gl_detail, _stereo._enable ? "Render Stereo" : "Render"); + PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); renderPassDraw(batch); } #ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo._enable) { + if (_stereo.isStereo()) { glDisable(GL_CLIP_DISTANCE0); } #endif @@ -358,6 +369,15 @@ void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); } + +void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { + +} + +void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) { + +} + void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); f(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 1c6cffb575..96217555e1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -143,6 +143,9 @@ public: // Reset stages virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; + virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; + virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp index 5c6a18d7af..358b90ed8b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp @@ -48,7 +48,7 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { } void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { - if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { + if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) { qWarning("Clear without scissor in stereo mode"); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp index a7d4a7ff7c..24f90395d7 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp @@ -322,7 +322,7 @@ void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) { Vec4i rect; memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); - if (_stereo._enable) { + if (_stereo.isStereo()) { rect.z /= 2; if (_stereo._pass) { rect.x += rect.z; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp index f3da18ba1d..01f055e0d9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp @@ -37,7 +37,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) glViewport(vp.x, vp.y, vp.z, vp.w); // Where we assign the GL viewport - if (_stereo._enable) { + if (_stereo.isStereo()) { vp.z /= 2; if (_stereo._pass) { vp.x += vp.z; @@ -119,7 +119,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo size_t offset = _cameraUboSize * _cameras.size(); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); - if (stereo._enable) { + if (stereo.isStereo()) { #ifdef GPU_STEREO_CAMERA_BUFFER _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); #else @@ -151,7 +151,7 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta #ifdef GPU_STEREO_CAMERA_BUFFER bindCurrentCamera(0); #else - if (!stereo._enable) { + if (!stereo.isStereo()) { bindCurrentCamera(0); } #endif diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 0d5036c202..15c0dfce49 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -390,6 +390,15 @@ void Batch::resetStages() { ADD_COMMAND(resetStages); } + +void Batch::disableContextStereo() { + ADD_COMMAND(disableContextStereo); +} + +void Batch::restoreContextStereo() { + ADD_COMMAND(restoreContextStereo); +} + void Batch::runLambda(std::function f) { ADD_COMMAND(runLambda); _params.emplace_back(_lambdas.cache(f)); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 85e99951cb..27c9402131 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -217,6 +217,9 @@ public: // Reset the stage caches and states void resetStages(); + void disableContextStereo(); + void restoreContextStereo(); + // Debugging void pushProfileRange(const char* name); void popProfileRange(); @@ -301,6 +304,9 @@ public: COMMAND_resetStages, + COMMAND_disableContextStereo, + COMMAND_restoreContextStereo, + COMMAND_runLambda, COMMAND_startNamedCall, @@ -467,7 +473,7 @@ public: NamedBatchDataMap _namedData; bool _enableStereo{ true }; - bool _enableSkybox{ false }; + bool _enableSkybox { false }; protected: friend class Context; diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 2116ffd6fe..24128524da 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -145,7 +145,7 @@ void Context::enableStereo(bool enable) { } bool Context::isStereo() { - return _stereo._enable; + return _stereo.isStereo(); } void Context::setStereoProjections(const mat4 eyeProjections[2]) { diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index de818ddcb0..7b7575e9ed 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -118,7 +118,7 @@ public: protected: virtual bool isStereo() { - return _stereo._enable; + return _stereo.isStereo(); } void getStereoProjections(mat4* eyeProjections) const { diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index ce95e8657e..88800652a5 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -93,7 +93,11 @@ namespace gpu { using TextureViews = std::vector; struct StereoState { + bool isStereo() const { + return _enable && !_contextDisable; + } bool _enable{ false }; + bool _contextDisable { false }; bool _skybox{ false }; // 0 for left eye, 1 for right eye uint8 _pass{ 0 }; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 788ec54a47..3b1a12cba7 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -16,6 +16,8 @@ using namespace ktx; +int ktxDescriptorMetaTypeId = qRegisterMetaType(); + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index b02e2ada75..8dc4ec7a47 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -387,4 +387,6 @@ namespace ktx { } +Q_DECLARE_METATYPE(ktx::KTXDescriptor*); + #endif // hifi_ktx_KTX_h diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 15eb4be839..06f0da1d09 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -13,6 +13,8 @@ #include +#include + #include #include #include @@ -50,6 +52,9 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; +static const QString RESOURCE_SCHEME = "resource"; +static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame"); + static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models @@ -180,6 +185,9 @@ ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNum } NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) { + if (url.scheme() == RESOURCE_SCHEME) { + return getResourceTexture(url); + } TextureExtra extra = { type, content, maxNumPixels }; return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } @@ -265,6 +273,18 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh return QSharedPointer(texture, &Resource::deleter); } +NetworkTexture::NetworkTexture(const QUrl& url) : +Resource(url), +_type(), +_sourceIsKTX(false), +_maxNumPixels(100) +{ + _textureSource = std::make_shared(); + _lowestRequestedMipLevel = 0; + _loaded = true; +} + + NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), @@ -303,14 +323,12 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, _width = texture->getWidth(); _height = texture->getHeight(); setSize(texture->getStoredSize()); + finishedLoading(true); } else { - // FIXME: If !gpuTexture, we failed to load! _width = _height = 0; - qWarning() << "Texture did not load"; + finishedLoading(false); } - finishedLoading(true); - emit networkTextureCreated(qWeakPointerCast (_self)); } @@ -382,8 +400,7 @@ void NetworkTexture::makeRequest() { emit loading(); - connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); - connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); + connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished); _bytesReceived = _bytesTotal = _bytes = 0; @@ -407,18 +424,18 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::startRequestForNextMipLevel() { - if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip - << " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url; + auto self = _self.lock(); + if (!self) { return; } - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { - auto self = _self.lock(); - if (!self) { - return; - } + auto texture = _textureSource->getGPUTexture(); + if (!texture || _ktxResourceState != WAITING_FOR_MIP_REQUEST) { + return; + } + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { _ktxResourceState = PENDING_MIP_REQUEST; init(false); @@ -453,6 +470,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { ByteRange range; range.fromInclusive = -HIGH_MIP_MAX_SIZE; _ktxMipRequest->setByteRange(range); + + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished); } else { ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData @@ -460,229 +479,315 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); - } - connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); - connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + } _ktxMipRequest->send(); } -void NetworkTexture::ktxHeaderRequestFinished() { - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - - if (!_ktxHeaderRequest) { +// This is called when the header or top mips have been loaded +void NetworkTexture::ktxInitialDataRequestFinished() { + if (!_ktxHeaderRequest || _ktxHeaderRequest->getState() != ResourceRequest::Finished || + !_ktxMipRequest || _ktxMipRequest->getState() != ResourceRequest::Finished) { + // Wait for both request to be finished return; } - _ktxHeaderRequestFinished = true; - maybeHandleFinishedInitialLoad(); + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished"); + + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxHeaderRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + setSize(_bytesTotal); + + TextureCache::requestCompleted(_self); + + auto result = _ktxHeaderRequest->getResult(); + if (result == ResourceRequest::Success) { + result = _ktxMipRequest->getResult(); + } + + if (result == ResourceRequest::Success) { + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + _ktxHeaderData = _ktxHeaderRequest->getData(); + _ktxHighMipData = _ktxMipRequest->getData(); + handleFinishedInitialLoad(); + } else { + if (handleFailedRequest(result)) { + _ktxResourceState = PENDING_INITIAL_LOAD; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } + } + + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } void NetworkTexture::ktxMipRequestFinished() { - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); + Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished"); + Q_ASSERT(_ktxResourceState == REQUESTING_MIP); - if (!_ktxMipRequest) { + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxMipRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + + setSize(_bytesTotal); + + if (!_ktxMipRequest || _ktxMipRequest != sender()) { + // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. + qWarning(networking) << "Received signal NetworkTexture::ktxMipRequestFinished from ResourceRequest that is not the current" + << " request: " << sender() << ", " << _ktxMipRequest; return; } - if (_ktxResourceState == LOADING_INITIAL_DATA) { - _ktxHighMipRequestFinished = true; - maybeHandleFinishedInitialLoad(); - } else if (_ktxResourceState == REQUESTING_MIP) { - Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); - if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + auto result = _ktxMipRequest->getResult(); + if (result == ResourceRequest::Success) { + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + if (_ktxResourceState == REQUESTING_MIP) { + Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + + auto self = _self; + auto url = _url; + auto data = _ktxMipRequest->getData(); + auto mipLevel = _ktxMipLevelRangeInFlight.first; auto texture = _textureSource->getGPUTexture(); - if (texture) { - texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, - _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); + DependencyManager::get()->incrementStat("PendingProcessing"); + QtConcurrent::run(QThreadPool::globalInstance(), [self, data, mipLevel, url, texture] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); - if (texture->minAvailableMipLevel() <= _ktxMipLevelRangeInFlight.first) { - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } else { - qWarning(networking) << "Failed to load mip: " << _url << ":" << _ktxMipLevelRangeInFlight.first; - _ktxResourceState = FAILED_TO_LOAD; + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; } - } else { - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - qWarning(networking) << "Trying to update mips but texture is null"; - } - finishedLoading(true); + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); + + auto resource = self.lock(); + if (!resource) { + // Resource no longer exists, bail + return; + } + + Q_ASSERT_X(texture, "Async - NetworkTexture::ktxMipRequestFinished", "NetworkTexture should have been assigned a GPU texture by now."); + + texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); + + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + + QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); + }); } else { + qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; finishedLoading(false); - if (handleFailedRequest(_ktxMipRequest->getResult())) { - _ktxResourceState = PENDING_MIP_REQUEST; - } else { - qWarning(networking) << "Failed to load mip: " << _url; - _ktxResourceState = FAILED_TO_LOAD; - } - } - - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { - startRequestForNextMipLevel(); } } else { - qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; + if (handleFailedRequest(result)) { + _ktxResourceState = PENDING_MIP_REQUEST; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } } + + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } -// This is called when the header or top mips have been loaded -void NetworkTexture::maybeHandleFinishedInitialLoad() { +// This is called when the header and top mips have been loaded +void NetworkTexture::handleFinishedInitialLoad() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()); - if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { + // create ktx... + auto ktxHeaderData = _ktxHeaderData; + auto ktxHighMipData = _ktxHighMipData; + _ktxHeaderData.clear(); + _ktxHighMipData.clear(); - TextureCache::requestCompleted(_self); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; - if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { - if (handleFailedRequest(_ktxMipRequest->getResult())) { - _ktxResourceState = PENDING_INITIAL_LOAD; - } - else { - _ktxResourceState = FAILED_TO_LOAD; - } + auto self = _self; + auto url = _url; + DependencyManager::get()->incrementStat("PendingProcessing"); + QtConcurrent::run(QThreadPool::globalInstance(), [self, ktxHeaderData, ktxHighMipData, url] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - } else { - // create ktx... - auto ktxHeaderData = _ktxHeaderRequest->getData(); - auto ktxHighMipData = _ktxMipRequest->getData(); - - auto header = reinterpret_cast(ktxHeaderData.data()); - - if (!ktx::checkIdentifier(header->identifier)) { - qWarning() << "Cannot load " << _url << ", invalid header identifier"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } - - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - if (imageDescriptors.size() == 0) { - qWarning(networking) << "Failed to process ktx file " << _url; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - } - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; - }); - std::string filename; - std::string hash; - if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { - qWarning("Invalid source hash key found, bailing"); - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } else { - // at this point the source hash is in binary 16-byte form - // and we need it in a hexadecimal string - auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); - hash = filename = binaryHash.toHex().toStdString(); - } - - auto textureCache = DependencyManager::get(); - - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - - if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); - if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - _file = ktxFile; - } - } - } - - if (!texture) { - - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - finishedLoading(false); - return; - } - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } else { - _file = file; - } - - auto newKtxDescriptor = memKtx->toDescriptor(); - - texture = gpu::Texture::build(newKtxDescriptor); - texture->setKtxBacking(file); - texture->setSource(filename); - - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - for (int level = static_cast(images.size()) - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; - } - ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); - ktxData -= ktx::IMAGE_SIZE_WIDTH; - imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); - } - - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); - } - - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; } - startRequestForNextMipLevel(); - } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); + + auto resource = self.lock(); + if (!resource) { + // Resource no longer exists, bail + return; + } + + auto header = reinterpret_cast(ktxHeaderData.data()); + + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << url << ", invalid header identifier"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { + qWarning() << "Cannot load " << url << ", did not receive all kv data with initial request"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << url; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + auto originalKtxDescriptor = new ktx::KTXDescriptor(*header, keyValues, imageDescriptors); + QMetaObject::invokeMethod(resource.data(), "setOriginalDescriptor", + Q_ARG(ktx::KTXDescriptor*, originalKtxDescriptor)); + + // Create bare ktx in memory + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; + }); + std::string filename; + std::string hash; + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { + qWarning("Invalid source hash key found, bailing"); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } else { + // at this point the source hash is in binary 16-byte form + // and we need it in a hexadecimal string + auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); + } + + auto textureCache = DependencyManager::get(); + + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + texture = gpu::Texture::unserialize(ktxFile); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } + } + } + + if (!texture) { + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << url << " failed to write cache file"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto newKtxDescriptor = memKtx->toDescriptor(); + + texture = gpu::Texture::build(newKtxDescriptor); + texture->setKtxBacking(file); + texture->setSource(filename); + + auto& images = originalKtxDescriptor->images; + size_t imageSizeRemaining = ktxHighMipData.size(); + const uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (int level = static_cast(images.size()) - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + ktxData -= image._imageSize; + texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + ktxData -= ktx::IMAGE_SIZE_WIDTH; + imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + texture = textureCache->cacheTextureByHash(filename, texture); + } + + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + + QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); + }); } void NetworkTexture::downloadFinished(const QByteArray& data) { @@ -845,11 +950,11 @@ void ImageReader::read() { const char* data = reinterpret_cast(memKtx->_storage->data()); size_t length = memKtx->_storage->size(); auto& ktxCache = textureCache->_ktxCache; - networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); // - if (!networkTexture->_file) { + auto file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); + if (!file) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { - texture->setKtxBacking(networkTexture->_file); + texture->setKtxBacking(file); } } else { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; @@ -866,3 +971,32 @@ void ImageReader::read() { Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); } + + +NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) { + gpu::TexturePointer texture; + if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) { + if (!_spectatorCameraNetworkTexture) { + _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); + } + texture = _spectatorCameraFramebuffer->getRenderBuffer(0); + if (texture) { + _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); + return _spectatorCameraNetworkTexture; + } + } + + return NetworkTexturePointer(); +} + +const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer() { + if (!_spectatorCameraFramebuffer) { + resetSpectatorCameraFramebuffer(2048, 1024); + } + return _spectatorCameraFramebuffer; +} + +void TextureCache::resetSpectatorCameraFramebuffer(int width, int height) { + _spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height)); + _spectatorCameraNetworkTexture.reset(); +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 7dab18d457..871a15a85e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -43,6 +43,7 @@ class NetworkTexture : public Resource, public Texture { Q_OBJECT public: + NetworkTexture(const QUrl& url); NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); ~NetworkTexture() override; @@ -58,14 +59,13 @@ public: void refresh() override; + Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); } + signals: void networkTextureCreated(const QWeakPointer& self); public slots: - void ktxHeaderRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } - void ktxHeaderRequestFinished(); - - void ktxMipRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxInitialDataRequestFinished(); void ktxMipRequestFinished(); protected: @@ -74,14 +74,14 @@ protected: virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; - + Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); - void startRequestForNextMipLevel(); + Q_INVOKABLE void startRequestForNextMipLevel(); void startMipRangeRequest(uint16_t low, uint16_t high); - void maybeHandleFinishedInitialLoad(); + void handleFinishedInitialLoad(); private: friend class KTXReader; @@ -102,16 +102,13 @@ private: bool _sourceIsKTX { false }; KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD }; - // TODO Can this be removed? - KTXFilePointer _file; - // The current mips that are currently being requested w/ _ktxMipRequest std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _ktxHeaderRequestFinished{ false }; - bool _ktxHighMipRequestFinished{ false }; + QByteArray _ktxHeaderData; + QByteArray _ktxHighMipData; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; @@ -128,6 +125,8 @@ private: int _width { 0 }; int _height { 0 }; int _maxNumPixels { ABSOLUTE_MAX_TEXTURE_NUM_PIXELS }; + + friend class TextureCache; }; using NetworkTexturePointer = QSharedPointer; @@ -166,6 +165,12 @@ public: gpu::TexturePointer getTextureByHash(const std::string& hash); gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); + + /// SpectatorCamera rendering targets. + NetworkTexturePointer getResourceTexture(QUrl resourceTextureUrl); + const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); + void resetSpectatorCameraFramebuffer(int width, int height); + protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); @@ -183,6 +188,7 @@ private: static const std::string KTX_DIRNAME; static const std::string KTX_EXT; + KTXCache _ktxCache; // Map from image hashes to texture weak pointers std::unordered_map> _texturesByHashes; @@ -193,6 +199,9 @@ private: gpu::TexturePointer _grayTexture; gpu::TexturePointer _blueTexture; gpu::TexturePointer _blackTexture; + + NetworkTexturePointer _spectatorCameraNetworkTexture; + gpu::FramebufferPointer _spectatorCameraFramebuffer; }; #endif // hifi_TextureCache_h diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 054557e920..3faa9981dc 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -353,13 +353,20 @@ void AssetClient::handleAssetGetReply(QSharedPointer message, S connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) { handleProgressCallback(weakNode, messageID, size, length); }); - connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() { - handleCompleteCallback(weakNode, messageID); + connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID, length]() { + handleCompleteCallback(weakNode, messageID, length); }); if (message->isComplete()) { disconnect(message.data(), nullptr, this, nullptr); - callbacks.completeCallback(true, error, message->readAll()); + + if (length != message->getBytesLeftToRead()) { + callbacks.completeCallback(false, error, QByteArray()); + } else { + callbacks.completeCallback(true, error, message->readAll()); + } + + messageCallbackMap.erase(requestIt); } } @@ -391,7 +398,7 @@ void AssetClient::handleProgressCallback(const QWeakPointer& node, Message callbacks.progressCallback(size, length); } -void AssetClient::handleCompleteCallback(const QWeakPointer& node, MessageID messageID) { +void AssetClient::handleCompleteCallback(const QWeakPointer& node, MessageID messageID, DataOffset length) { auto senderNode = node.toStrongRef(); if (!senderNode) { @@ -424,8 +431,7 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message return; } - - if (message->failed()) { + if (message->failed() || length != message->getBytesLeftToRead()) { callbacks.completeCallback(false, AssetServerError::NoError, QByteArray()); } else { callbacks.completeCallback(true, AssetServerError::NoError, message->readAll()); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 6f9cc3cd31..cab4a4f5b0 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -93,7 +93,7 @@ private: bool cancelUploadAssetRequest(MessageID id); void handleProgressCallback(const QWeakPointer& node, MessageID messageID, qint64 size, DataOffset length); - void handleCompleteCallback(const QWeakPointer& node, MessageID messageID); + void handleCompleteCallback(const QWeakPointer& node, MessageID messageID, DataOffset length); void forceFailureOfPendingRequests(SharedNodePointer node); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 00fa3d9f2f..7fa563d4ad 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -104,12 +104,7 @@ void AssetRequest::start() { break; } } else { - if (_byteRange.isSet()) { - // we had a byte range, the size of the data does not match what we expect, so we return an error - if (data.size() != _byteRange.size()) { - _error = SizeVerificationFailed; - } - } else if (hashData(data).toHex() != _hash) { + if (!_byteRange.isSet() && hashData(data).toHex() != _hash) { // the hash of the received data does not match what we expect, so we return an error _error = HashVerificationFailed; } diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 297bdb2cca..7bfdbddbc5 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -184,6 +184,9 @@ public: // will query the underlying hmd api to compute the most recent head pose virtual bool beginFrameRender(uint32_t frameIndex) { return true; } + // Set the texture to display on the monitor and return true, if allowed. Empty string resets. + virtual bool setDisplayTexture(const QString& name) { return false; } + virtual float devicePixelRatio() { return 1.0f; } // Rate at which we render frames virtual float renderRate() const { return -1.0f; } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index f3ee846d39..07628904f1 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -29,7 +29,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE); + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE); if (useCauterizedMesh) { ModelPointer model = _model.lock(); if (model) { diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index 7e04b1c2a4..e35120eb5b 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -548,6 +548,7 @@ glm::ivec3 LightClusters::updateClusters() { LightClusteringPass::LightClusteringPass() { + _lightClusters = std::make_shared(); } @@ -566,12 +567,7 @@ void LightClusteringPass::run(const render::RenderContextPointer& renderContext, auto deferredTransform = inputs.get0(); auto lightingModel = inputs.get1(); auto surfaceGeometryFramebuffer = inputs.get2(); - - - if (!_lightClusters) { - _lightClusters = std::make_shared(); - } - + // first update the Grid with the new frustum if (!_freeze) { _lightClusters->updateFrustum(args->getViewFrustum()); diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 901e0db22f..a75488ce7a 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -61,7 +61,7 @@ namespace render { class Args { public: - enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE }; + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; enum DebugFlags { RENDER_DEBUG_NONE = 0, diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 3ebc80d2f1..86f55ef6aa 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -13,6 +13,7 @@ import "configSlider" import "../lib/plotperf" Column { + property var mainViewTask: Render.getConfig("RenderMainView") spacing: 8 Column { id: surfaceGeometry @@ -32,7 +33,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: (modelData.split(":")[3] == 'true') - config: Render.getConfig("AmbientOcclusion") + config: mainViewTask.getConfig("AmbientOcclusion") property: modelData.split(":")[1] max: modelData.split(":")[2] min: 0.0 @@ -50,8 +51,8 @@ Column { ] CheckBox { text: qsTr(modelData.split(":")[0]) - checked: Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] - onCheckedChanged: { Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked } + checked: mainViewTask.getConfig("AmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { mainViewTask.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked } } } } @@ -62,8 +63,8 @@ Column { ] CheckBox { text: qsTr(modelData.split(":")[0]) - checked: Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] - onCheckedChanged: { Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] = checked } + checked: mainViewTask.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { mainViewTask.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] = checked } } } } @@ -72,7 +73,7 @@ Column { PlotPerf { title: "Timing" height: 50 - object: Render.getConfig("AmbientOcclusion") + object: mainViewTask.getConfig("AmbientOcclusion") valueUnit: "ms" valueScale: 1 valueNumDigits: "3" diff --git a/scripts/developer/utilities/render/culling.qml b/scripts/developer/utilities/render/culling.qml index e3f5e67bbe..3c3c0f67d9 100644 --- a/scripts/developer/utilities/render/culling.qml +++ b/scripts/developer/utilities/render/culling.qml @@ -14,8 +14,9 @@ import "configSlider" Column { id: root spacing: 8 - property var sceneOctree: Render.getConfig("DrawSceneOctree"); - property var itemSelection: Render.getConfig("DrawItemSelection"); + property var mainViewTask: Render.getConfig("RenderMainView"); + property var sceneOctree: mainViewTask.getConfig("DrawSceneOctree"); + property var itemSelection: mainViewTask.getConfig("DrawItemSelection"); Component.onCompleted: { sceneOctree.enabled = true; @@ -30,8 +31,8 @@ Column { Component.onDestruction: { sceneOctree.enabled = false; itemSelection.enabled = false; - Render.getConfig("FetchSceneSelection").freezeFrustum = false; - Render.getConfig("CullSceneSelection").freezeFrustum = false; + mainViewTask.getConfig("FetchSceneSelection").freezeFrustum = false; + mainViewTask.getConfig("CullSceneSelection").freezeFrustum = false; } GroupBox { @@ -45,8 +46,8 @@ Column { text: "Freeze Culling Frustum" checked: false onCheckedChanged: { - Render.getConfig("FetchSceneSelection").freezeFrustum = checked; - Render.getConfig("CullSceneSelection").freezeFrustum = checked; + mainViewTask.getConfig("FetchSceneSelection").freezeFrustum = checked; + mainViewTask.getConfig("CullSceneSelection").freezeFrustum = checked; } } Label { @@ -103,7 +104,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: true - config: Render.getConfig(modelData.split(":")[1]) + config: mainViewTask.getConfig(modelData.split(":")[1]) property: "maxDrawn" max: config.numDrawn min: -1 diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index c57fdf0526..e93d153486 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -34,5 +34,5 @@ function setDebugCursor(x, y) { nx = (x / Window.innerWidth); ny = 1.0 - ((y) / (Window.innerHeight - 32)); - Render.getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; + Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; } diff --git a/scripts/developer/utilities/render/debugSubsurfaceScattering.js b/scripts/developer/utilities/render/debugSubsurfaceScattering.js index 72b15546e0..c578ef0f06 100644 --- a/scripts/developer/utilities/render/debugSubsurfaceScattering.js +++ b/scripts/developer/utilities/render/debugSubsurfaceScattering.js @@ -33,5 +33,5 @@ function setDebugCursor(x, y) { nx = (x / Window.innerWidth); ny = 1.0 - ((y) / (Window.innerHeight - 32)); - Render.getConfig("DebugScattering").debugCursorTexcoord = { x: nx, y: ny }; + Render.getConfig("RenderMainView").getConfig("DebugScattering").debugCursorTexcoord = { x: nx, y: ny }; } diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index ff4621a87a..2254b6d95f 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -13,6 +13,7 @@ import "configSlider" Column { spacing: 8 + property var mainViewTask: Render.getConfig("RenderMainView") Row { spacing: 8 @@ -29,8 +30,8 @@ Column { ] CheckBox { text: modelData.split(":")[0] - checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -49,8 +50,8 @@ Column { ] CheckBox { text: modelData.split(":")[0] - checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -69,8 +70,8 @@ Column { ] CheckBox { text: modelData.split(":")[0] - checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -83,7 +84,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: false - config: Render.getConfig(modelData.split(":")[1]) + config: mainViewTask.getConfig(modelData.split(":")[1]) property: modelData.split(":")[2] max: modelData.split(":")[3] min: modelData.split(":")[4] @@ -107,7 +108,7 @@ Column { ListElement { text: "Filmic"; color: "White" } } width: 200 - onCurrentIndexChanged: { Render.getConfig("ToneMapping")["curve"] = currentIndex } + onCurrentIndexChanged: { mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex } } } } @@ -120,7 +121,7 @@ Column { anchors.left: root.left } - property var config: Render.getConfig("DebugDeferredBuffer") + property var config: mainViewTask.getConfig("DebugDeferredBuffer") function setDebugMode(mode) { framebuffer.config.enabled = (mode != 0); @@ -168,40 +169,40 @@ Column { CheckBox { text: "Opaques" - checked: Render.getConfig("DrawOpaqueBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawOpaqueBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked } } CheckBox { text: "Transparents" - checked: Render.getConfig("DrawTransparentBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawTransparentBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawTransparentBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked } } CheckBox { text: "Overlay Opaques" - checked: Render.getConfig("DrawOverlayOpaqueBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawOverlayOpaqueBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawOverlayOpaqueBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawOverlayOpaqueBounds")["enabled"] = checked } } CheckBox { text: "Overlay Transparents" - checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawOverlayTransparentBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked } } } Column { CheckBox { text: "Metas" - checked: Render.getConfig("DrawMetaBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawMetaBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked } } CheckBox { text: "Lights" - checked: Render.getConfig("DrawLightBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawLightBounds")["enabled"] = checked; } + checked: mainViewTask.getConfig("DrawLightBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; } } CheckBox { text: "Zones" - checked: Render.getConfig("DrawZones")["enabled"] - onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; } + checked: mainViewTask.getConfig("DrawZones")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; mainViewTask.getConfig("DrawZones")["enabled"] = checked; } } } } diff --git a/scripts/developer/utilities/render/lightClustering.qml b/scripts/developer/utilities/render/lightClustering.qml index 4db7aa8c39..930fd79db3 100644 --- a/scripts/developer/utilities/render/lightClustering.qml +++ b/scripts/developer/utilities/render/lightClustering.qml @@ -17,18 +17,19 @@ Column { Column { id: lightClustering spacing: 10 + property var mainViewTask: Render.getConfig("RenderMainView"); Column{ PlotPerf { title: "Light CLustering Timing" height: 50 - object: Render.getConfig("LightClustering") + object: mainViewTask.getConfig("LightClustering") valueUnit: "ms" valueScale: 1 valueNumDigits: "4" plots: [ { - object: Render.getConfig("LightClustering"), + object: mainViewTask.getConfig("LightClustering"), prop: "cpuRunTime", label: "time", scale: 1, @@ -40,19 +41,19 @@ Column { PlotPerf { title: "Lights" height: 50 - object: Render.getConfig("LightClustering") + object: mainViewTask.getConfig("LightClustering") valueUnit: "" valueScale: 1 valueNumDigits: "0" plots: [ { - object: Render.getConfig("LightClustering"), + object: mainViewTask.getConfig("LightClustering"), prop: "numClusteredLights", label: "visible", color: "#D959FE" }, { - object: Render.getConfig("LightClustering"), + object: mainViewTask.getConfig("LightClustering"), prop: "numInputLights", label: "input", color: "#FED959" @@ -63,25 +64,25 @@ Column { PlotPerf { title: "Scene Lights" height: 80 - object: Render.getConfig("LightClustering") + object: mainViewTask.getConfig("LightClustering") valueUnit: "" valueScale: 1 valueNumDigits: "0" plots: [ { - object: Render.getConfig("LightClustering"), + object: mainViewTask.getConfig("LightClustering"), prop: "numSceneLights", label: "current", color: "#00B4EF" }, { - object: Render.getConfig("LightClustering"), + object: mainViewTask.getConfig("LightClustering"), prop: "numFreeSceneLights", label: "free", color: "#1AC567" }, { - object: Render.getConfig("LightClustering"), + object: mainViewTask.getConfig("LightClustering"), prop: "numAllocatedSceneLights", label: "allocated", color: "#9495FF" @@ -92,7 +93,7 @@ Column { ConfigSlider { label: qsTr("Range Near [m]") integral: false - config: Render.getConfig("LightClustering") + config: mainViewTask.getConfig("LightClustering") property: "rangeNear" max: 20.0 min: 0.1 @@ -100,7 +101,7 @@ Column { ConfigSlider { label: qsTr("Range Far [m]") integral: false - config: Render.getConfig("LightClustering") + config: mainViewTask.getConfig("LightClustering") property: "rangeFar" max: 500.0 min: 100.0 @@ -108,7 +109,7 @@ Column { ConfigSlider { label: qsTr("Grid X") integral: true - config: Render.getConfig("LightClustering") + config: mainViewTask.getConfig("LightClustering") property: "dimX" max: 32 min: 1 @@ -116,7 +117,7 @@ Column { ConfigSlider { label: qsTr("Grid Y") integral: true - config: Render.getConfig("LightClustering") + config: mainViewTask.getConfig("LightClustering") property: "dimY" max: 32 min: 1 @@ -124,33 +125,33 @@ Column { ConfigSlider { label: qsTr("Grid Z") integral: true - config: Render.getConfig("LightClustering") + config: mainViewTask.getConfig("LightClustering") property: "dimZ" max: 31 min: 1 } CheckBox { text: "Freeze" - checked: Render.getConfig("LightClustering")["freeze"] - onCheckedChanged: { Render.getConfig("LightClustering")["freeze"] = checked } + checked: mainViewTask.getConfig("LightClustering")["freeze"] + onCheckedChanged: { mainViewTask.getConfig("LightClustering")["freeze"] = checked } } CheckBox { text: "Draw Grid" - checked: Render.getConfig("DebugLightClusters")["doDrawGrid"] - onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawGrid"] = checked } + checked: mainViewTask.getConfig("DebugLightClusters")["doDrawGrid"] + onCheckedChanged: { mainViewTask.getConfig("DebugLightClusters")["doDrawGrid"] = checked } } CheckBox { text: "Draw Cluster From Depth" - checked: Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] - onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] = checked } + checked: mainViewTask.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] + onCheckedChanged: { mainViewTask.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] = checked } } CheckBox { text: "Draw Content" - checked: Render.getConfig("DebugLightClusters")["doDrawContent"] - onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawContent"] = checked } + checked: mainViewTask.getConfig("DebugLightClusters")["doDrawContent"] + onCheckedChanged: { mainViewTask.getConfig("DebugLightClusters")["doDrawContent"] = checked } } Label { - text: "Num Cluster Items = " + Render.getConfig("LightClustering")["numClusteredLightReferences"].toFixed(0) + text: "Num Cluster Items = " + mainViewTask.getConfig("LightClustering")["numClusteredLightReferences"].toFixed(0) } } diff --git a/scripts/developer/utilities/render/stats.qml b/scripts/developer/utilities/render/stats.qml index 54e0dc4ce8..064045e8f5 100644 --- a/scripts/developer/utilities/render/stats.qml +++ b/scripts/developer/utilities/render/stats.qml @@ -21,7 +21,8 @@ Item { spacing: 8 anchors.fill:parent - property var config: Render.getConfig("Stats") + property var mainViewTask: Render.getConfig("RenderMainView"); + property var config: mainViewTask.getConfig("Stats") function evalEvenHeight() { // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ? @@ -182,9 +183,9 @@ Item { ] } - property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred") - property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred") - property var drawLightConfig: Render.getConfig("DrawLight") + property var drawOpaqueConfig: mainViewTask.getConfig("DrawOpaqueDeferred") + property var drawTransparentConfig: mainViewTask.getConfig("DrawTransparentDeferred") + property var drawLightConfig: mainViewTask.getConfig("DrawLight") PlotPerf { title: "Items" @@ -199,13 +200,13 @@ Item { color: "#1AC567" }, { - object: Render.getConfig("DrawTransparentDeferred"), + object: mainViewTask.getConfig("DrawTransparentDeferred"), prop: "numDrawn", label: "Translucents", color: "#00B4EF" }, { - object: Render.getConfig("DrawLight"), + object: mainViewTask.getConfig("DrawLight"), prop: "numDrawn", label: "Lights", color: "#FED959" @@ -222,25 +223,25 @@ Item { valueNumDigits: "2" plots: [ { - object: Render.getConfig("DrawOpaqueDeferred"), + object: mainViewTask.getConfig("DrawOpaqueDeferred"), prop: "cpuRunTime", label: "Opaques", color: "#1AC567" }, { - object: Render.getConfig("DrawTransparentDeferred"), + object: mainViewTask.getConfig("DrawTransparentDeferred"), prop: "cpuRunTime", label: "Translucents", color: "#00B4EF" }, { - object: Render.getConfig("RenderDeferred"), + object: mainViewTask.getConfig("RenderDeferred"), prop: "cpuRunTime", label: "Lighting", color: "#FED959" }, { - object: Render.getConfig("RenderDeferredTask"), + object: mainViewTask.getConfig("RenderDeferredTask"), prop: "cpuRunTime", label: "RenderFrame", color: "#E2334D" diff --git a/scripts/developer/utilities/render/statsGPU.qml b/scripts/developer/utilities/render/statsGPU.qml index 3d23c2c6dc..b3f5ec6e45 100644 --- a/scripts/developer/utilities/render/statsGPU.qml +++ b/scripts/developer/utilities/render/statsGPU.qml @@ -21,7 +21,8 @@ Item { spacing: 8 anchors.fill:parent - property var config: Render.getConfig("Stats") + property var mainViewTask: Render.getConfig("RenderMainView"); + property var config: mainViewTask.getConfig("Stats") function evalEvenHeight() { // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ? @@ -38,31 +39,31 @@ Item { valueNumDigits: "4" plots: [ { - object: Render.getConfig("OpaqueRangeTimer"), + object: mainViewTask.getConfig("OpaqueRangeTimer"), prop: "gpuRunTime", label: "Opaque", color: "#FFFFFF" }, { - object: Render.getConfig("LinearDepth"), + object: mainViewTask.getConfig("LinearDepth"), prop: "gpuRunTime", label: "LinearDepth", color: "#00FF00" },{ - object: Render.getConfig("SurfaceGeometry"), + object: mainViewTask.getConfig("SurfaceGeometry"), prop: "gpuRunTime", label: "SurfaceGeometry", color: "#00FFFF" }, { - object: Render.getConfig("RenderDeferred"), + object: mainViewTask.getConfig("RenderDeferred"), prop: "gpuRunTime", label: "DeferredLighting", color: "#FF00FF" } , { - object: Render.getConfig("ToneAndPostRangeTimer"), + object: mainViewTask.getConfig("ToneAndPostRangeTimer"), prop: "gpuRunTime", label: "tone and post", color: "#FF0000" @@ -78,31 +79,31 @@ Item { valueNumDigits: "3" plots: [ { - object: Render.getConfig("OpaqueRangeTimer"), + object: mainViewTask.getConfig("OpaqueRangeTimer"), prop: "batchRunTime", label: "Opaque", color: "#FFFFFF" }, { - object: Render.getConfig("LinearDepth"), + object: mainViewTask.getConfig("LinearDepth"), prop: "batchRunTime", label: "LinearDepth", color: "#00FF00" },{ - object: Render.getConfig("SurfaceGeometry"), + object: mainViewTask.getConfig("SurfaceGeometry"), prop: "batchRunTime", label: "SurfaceGeometry", color: "#00FFFF" }, { - object: Render.getConfig("RenderDeferred"), + object: mainViewTask.getConfig("RenderDeferred"), prop: "batchRunTime", label: "DeferredLighting", color: "#FF00FF" } , { - object: Render.getConfig("ToneAndPostRangeTimer"), + object: mainViewTask.getConfig("ToneAndPostRangeTimer"), prop: "batchRunTime", label: "tone and post", color: "#FF0000" diff --git a/scripts/developer/utilities/render/subsurfaceScattering.qml b/scripts/developer/utilities/render/subsurfaceScattering.qml index 47b960c98b..53bca7f2b8 100644 --- a/scripts/developer/utilities/render/subsurfaceScattering.qml +++ b/scripts/developer/utilities/render/subsurfaceScattering.qml @@ -16,28 +16,29 @@ Column { Column { id: scattering spacing: 10 + property var mainViewTask: Render.getConfig("RenderMainView"); Column{ CheckBox { text: "Scattering" - checked: Render.getConfig("Scattering").enableScattering - onCheckedChanged: { Render.getConfig("Scattering").enableScattering = checked } + checked: mainViewTask.getConfig("Scattering").enableScattering + onCheckedChanged: { mainViewTask.getConfig("Scattering").enableScattering = checked } } CheckBox { text: "Show Scattering BRDF" - checked: Render.getConfig("Scattering").showScatteringBRDF - onCheckedChanged: { Render.getConfig("Scattering").showScatteringBRDF = checked } + checked: mainViewTask.getConfig("Scattering").showScatteringBRDF + onCheckedChanged: { mainViewTask.getConfig("Scattering").showScatteringBRDF = checked } } CheckBox { text: "Show Curvature" - checked: Render.getConfig("Scattering").showCurvature - onCheckedChanged: { Render.getConfig("Scattering").showCurvature = checked } + checked: mainViewTask.getConfig("Scattering").showCurvature + onCheckedChanged: { mainViewTask.getConfig("Scattering").showCurvature = checked } } CheckBox { text: "Show Diffused Normal" - checked: Render.getConfig("Scattering").showDiffusedNormal - onCheckedChanged: { Render.getConfig("Scattering").showDiffusedNormal = checked } + checked: mainViewTask.getConfig("Scattering").showDiffusedNormal + onCheckedChanged: { mainViewTask.getConfig("Scattering").showDiffusedNormal = checked } } Repeater { model: [ "Scattering Bent Red:Scattering:bentRed:2.0", @@ -50,7 +51,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: false - config: Render.getConfig(modelData.split(":")[1]) + config: mainViewTask.getConfig(modelData.split(":")[1]) property: modelData.split(":")[2] max: modelData.split(":")[3] min: 0.0 @@ -58,23 +59,23 @@ Column { } CheckBox { text: "Scattering Profile" - checked: Render.getConfig("DebugScattering").showProfile - onCheckedChanged: { Render.getConfig("DebugScattering").showProfile = checked } + checked: mainViewTask.getConfig("DebugScattering").showProfile + onCheckedChanged: { mainViewTask.getConfig("DebugScattering").showProfile = checked } } CheckBox { text: "Scattering Table" - checked: Render.getConfig("DebugScattering").showLUT - onCheckedChanged: { Render.getConfig("DebugScattering").showLUT = checked } + checked: mainViewTask.getConfig("DebugScattering").showLUT + onCheckedChanged: { mainViewTask.getConfig("DebugScattering").showLUT = checked } } CheckBox { text: "Cursor Pixel" - checked: Render.getConfig("DebugScattering").showCursorPixel - onCheckedChanged: { Render.getConfig("DebugScattering").showCursorPixel = checked } + checked: mainViewTask.getConfig("DebugScattering").showCursorPixel + onCheckedChanged: { mainViewTask.getConfig("DebugScattering").showCursorPixel = checked } } CheckBox { text: "Skin Specular Beckmann" - checked: Render.getConfig("DebugScattering").showSpecularTable - onCheckedChanged: { Render.getConfig("DebugScattering").showSpecularTable = checked } + checked: mainViewTask.getConfig("DebugScattering").showSpecularTable + onCheckedChanged: { mainViewTask.getConfig("DebugScattering").showSpecularTable = checked } } } } diff --git a/scripts/developer/utilities/render/surfaceGeometryPass.qml b/scripts/developer/utilities/render/surfaceGeometryPass.qml index 1ff0efa15d..7e70dceef1 100644 --- a/scripts/developer/utilities/render/surfaceGeometryPass.qml +++ b/scripts/developer/utilities/render/surfaceGeometryPass.qml @@ -16,12 +16,13 @@ Column { Column { id: surfaceGeometry spacing: 10 + property var mainViewTask: Render.getConfig("RenderMainView"); Column{ ConfigSlider { label: qsTr("Depth Threshold [cm]") integral: false - config: Render.getConfig("SurfaceGeometry") + config: mainViewTask.getConfig("SurfaceGeometry") property: "depthThreshold" max: 5.0 min: 0.0 @@ -34,7 +35,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: (modelData.split(":")[3] == 'true') - config: Render.getConfig("SurfaceGeometry") + config: mainViewTask.getConfig("SurfaceGeometry") property: modelData.split(":")[1] max: modelData.split(":")[2] min: 0.0 @@ -42,8 +43,8 @@ Column { } CheckBox { text: "Half Resolution" - checked: Render.getConfig("SurfaceGeometry")["resolutionLevel"] - onCheckedChanged: { Render.getConfig("SurfaceGeometry")["resolutionLevel"] = checked } + checked: mainViewTask.getConfig("SurfaceGeometry")["resolutionLevel"] + onCheckedChanged: { mainViewTask.getConfig("SurfaceGeometry")["resolutionLevel"] = checked } } Repeater { @@ -53,7 +54,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: false - config: Render.getConfig(modelData.split(":")[1]) + config: mainViewTask.getConfig(modelData.split(":")[1]) property: modelData.split(":")[2] max: modelData.split(":")[3] min: 0.0 diff --git a/scripts/developer/utilities/render/textureMonitor.qml b/scripts/developer/utilities/render/textureMonitor.qml index 97cc577ff9..1a6dffab23 100644 --- a/scripts/developer/utilities/render/textureMonitor.qml +++ b/scripts/developer/utilities/render/textureMonitor.qml @@ -22,7 +22,8 @@ Item { spacing: 8 anchors.fill:parent - property var config: Render.getConfig("Stats") + property var mainViewTask: Render.getConfig("RenderMainView"); + property var config: mainViewTask.getConfig("Stats") function evalEvenHeight() { // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?