diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 4de09c1bf3..809f48361d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -571,6 +571,7 @@ Rectangle { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } RalewayBold { diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 145b78bdc4..8b0ec17232 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -45,6 +45,7 @@ Rectangle { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } Rectangle { diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 0d3f67ef7a..bcc641acd5 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -44,6 +44,7 @@ Rectangle { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } Item { diff --git a/interface/resources/qml/hifi/commerce/common/images/loader.gif b/interface/resources/qml/hifi/commerce/common/images/loader.gif new file mode 100644 index 0000000000..0536bd1884 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/common/images/loader.gif differ diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index c331532f5e..421fa4b074 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -119,6 +119,7 @@ Rectangle { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } Image { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index f7913e5b1e..05e280b839 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -316,6 +316,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } RalewayBold { diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 87430246f3..c586af9e80 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -66,6 +66,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } // This will cause a bug -- if you bring up passphrase selection in HUD mode while diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index 50e58f8cc4..50bea2a3cf 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -34,6 +34,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } Connections { diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index 634a68d117..224b743c06 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -306,6 +306,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } RalewayBold { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index fd74b07465..fab27a29bb 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -394,6 +394,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } Image { diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml index f66781c919..ee1246c0c4 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml @@ -43,6 +43,7 @@ Item { width: parent.width; height: (root.shouldShowTopAndBottomOfWallet || root.shouldShowTopOfWallet) ? parent.height : parent.height - root.parentAppTitleBarHeight - root.parentAppNavBarHeight; propagateComposedEvents: false; + hoverEnabled: true; } Connections { @@ -57,6 +58,9 @@ Item { if (result.status === 'success') { root.nextActiveView = 'paymentSuccess'; + if (sendPubliclyCheckbox.checked && sendMoneyStep.referrer === "nearby") { + sendSignalToWallet({method: 'sendMoney_sendPublicly', recipient: sendMoneyStep.selectedRecipientNodeID, amount: parseInt(amountTextField.text)}); + } } else { root.nextActiveView = 'paymentFailure'; } @@ -103,6 +107,12 @@ Item { } } + HifiCommerceCommon.CommerceLightbox { + id: lightboxPopup; + visible: false; + anchors.fill: parent; + } + // Send Money Home BEGIN Item { id: sendMoneyHome; @@ -326,6 +336,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } ListModel { @@ -477,6 +488,7 @@ Item { enabled: connectionsList.currentIndex !== index; anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; onClicked: { connectionsList.currentIndex = index; } @@ -505,6 +517,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } Rectangle { @@ -917,7 +930,7 @@ Item { id: optionalMessage; property int maximumLength: 72; property string previousText: text; - placeholderText: "Optional Message (" + maximumLength + " character limit)"; + placeholderText: "Optional Public Message (" + maximumLength + " character limit)"; font.family: firaSansSemiBold.name; font.pixelSize: 20; // Anchors @@ -967,16 +980,54 @@ Item { HifiControlsUit.CheckBox { id: sendPubliclyCheckbox; - visible: false; // FIXME ONCE PARTICLE EFFECTS ARE IN - text: "Send Publicly" + visible: sendMoneyStep.referrer === "nearby"; + checked: Settings.getValue("sendMoneyNearbyPublicly", true); + text: "Show Effect" // Anchors anchors.top: messageContainer.bottom; anchors.topMargin: 16; anchors.left: parent.left; anchors.leftMargin: 20; - anchors.right: parent.right; - anchors.rightMargin: 16; - boxSize: 24; + width: 110; + boxSize: 28; + onCheckedChanged: { + Settings.setValue("sendMoneyNearbyPublicly", checked); + } + } + RalewaySemiBold { + id: sendPubliclyCheckboxHelp; + visible: sendPubliclyCheckbox.visible; + text: "[?]"; + // Anchors + anchors.left: sendPubliclyCheckbox.right; + anchors.leftMargin: 8; + anchors.verticalCenter: sendPubliclyCheckbox.verticalCenter; + height: 30; + width: paintedWidth; + // Text size + size: 18; + // Style + color: hifi.colors.blueAccent; + MouseArea { + enabled: sendPubliclyCheckboxHelp.visible; + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.color = hifi.colors.blueHighlight; + } + onExited: { + parent.color = hifi.colors.blueAccent; + } + onClicked: { + lightboxPopup.titleText = "Send Money Effect"; + lightboxPopup.bodyImageSource = "../wallet/sendMoney/images/send-money-effect-sm.jpg"; // Path relative to CommerceLightbox.qml + lightboxPopup.bodyText = "Enabling this option will create a particle effect between you and " + + "your recipient that is visible to everyone nearby."; + lightboxPopup.button1text = "CLOSE"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.visible = true; + } + } } Item { @@ -1059,6 +1110,7 @@ Item { MouseArea { anchors.fill: parent; propagateComposedEvents: false; + hoverEnabled: true; } AnimatedImage { @@ -1533,6 +1585,8 @@ Item { sendMoneyStep.selectedRecipientProfilePic = ""; amountTextField.text = ""; optionalMessage.text = ""; + sendPubliclyCheckbox.checked = Settings.getValue("sendMoneyNearbyPublicly", true); + sendMoneyStep.referrer = ""; } // diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/send-money-effect-sm.jpg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/send-money-effect-sm.jpg new file mode 100644 index 0000000000..7cabf9414a Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/send-money-effect-sm.jpg differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 99bd4d5758..1a54c94d53 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2294,11 +2294,7 @@ void Application::initializeGL() { #ifndef Q_OS_ANDROID _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); #endif - _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED); - -#ifdef Q_OS_OSX - DeadlockWatchdogThread::resume(); -#endif + _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); @@ -2306,6 +2302,10 @@ void Application::initializeGL() { // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. DependencyManager::get()->initializeShapePipelines(); +#ifdef Q_OS_OSX + DeadlockWatchdogThread::resume(); +#endif + _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->setObjectName("MainThreadContext"); _offscreenContext->create(_glWidget->qglContext()); diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 1231e5834b..e1f198eed2 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -178,7 +178,7 @@ public: render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; namespace render { - template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 5db34c9441..6b8e370689 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -19,8 +19,9 @@ 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); + + task.addJob("RenderShadowTask", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); + const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); assert(items.canCast()); if (!isDeferred) { task.addJob("Forward", items); @@ -205,7 +206,7 @@ public: void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { const auto cachedArg = task.addJob("SecondaryCamera"); - const auto items = task.addJob("FetchCullSort", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); assert(items.canCast()); if (!isDeferred) { task.addJob("Forward", items); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4ab741e32c..d2a9e798f8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include "MyHead.h" #include "MySkeletonModel.h" @@ -503,11 +504,42 @@ void MyAvatar::updateEyeContactTarget(float deltaTime) { extern QByteArray avatarStateToFrame(const AvatarData* _avatar); extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); +void MyAvatar::beParentOfChild(SpatiallyNestablePointer newChild) const { + _cauterizationNeedsUpdate = true; + SpatiallyNestable::beParentOfChild(newChild); +} + +void MyAvatar::forgetChild(SpatiallyNestablePointer newChild) const { + _cauterizationNeedsUpdate = true; + SpatiallyNestable::forgetChild(newChild); +} + +void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->setCauterized(!_prevShouldDrawHead); + } +} + void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); animateScaleChanges(deltaTime); + if (_cauterizationNeedsUpdate) { + const std::unordered_set& headBoneSet = _skeletonModel->getCauterizeBoneSet(); + forEachChild([&](SpatiallyNestablePointer object) { + bool isChildOfHead = headBoneSet.find(object->getParentJointIndex()) != headBoneSet.end(); + if (isChildOfHead) { + updateChildCauterization(object); + object->forEachDescendant([&](SpatiallyNestablePointer descendant) { + updateChildCauterization(descendant); + }); + } + }); + _cauterizationNeedsUpdate = false; + } + { PerformanceTimer perfTimer("transform"); bool stepAction = false; @@ -1067,7 +1099,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene()); + _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1417,8 +1449,9 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); - _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); + _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE); _headBoneSet.clear(); + _cauterizationNeedsUpdate = true; emit skeletonChanged(); } @@ -1762,7 +1795,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) { if (model->isActive() && model->isRenderable()) { - model->setVisibleInScene(visible, scene); + model->setVisibleInScene(visible, scene, render::ItemKey::TAG_BITS_NONE); } } @@ -1790,6 +1823,8 @@ void MyAvatar::initHeadBones() { } q.pop(); } + + _cauterizationNeedsUpdate = true; } QUrl MyAvatar::getAnimGraphOverrideUrl() const { @@ -1860,6 +1895,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); _isAnimatingScale = true; + _cauterizationNeedsUpdate = true; } if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) { @@ -1948,6 +1984,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { + _cauterizationNeedsUpdate = true; _skeletonModel->setEnableCauterization(!shouldDrawHead); for (int i = 0; i < _attachmentData.size(); i++) { @@ -1957,7 +1994,8 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { - _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene()); + _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(), + render::ItemKey::TAG_BITS_NONE); } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ed391f750a..97e82b87f5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -633,6 +633,11 @@ signals: private slots: void leaveDomain(); + +protected: + virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override; + virtual void forgetChild(SpatiallyNestablePointer newChild) const override; + private: bool requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& positionOut); @@ -812,6 +817,8 @@ private: bool _enableDebugDrawIKChains { false }; bool _enableDebugDrawDetailedCollision { false }; + mutable bool _cauterizationNeedsUpdate; // do we need to scan children and update their "cauterized" state? + AudioListenerMode _audioListenerMode; glm::vec3 _customListenPosition; glm::quat _customListenOrientation; @@ -849,6 +856,8 @@ private: // height of user in sensor space, when standing erect. ThreadSafeValueCache _userHeight { DEFAULT_AVATAR_HEIGHT }; + void updateChildCauterization(SpatiallyNestablePointer object); + // max unscaled forward movement speed ThreadSafeValueCache _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; }; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 310dbf78d8..4847650163 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -86,7 +86,8 @@ void ModelOverlay::update(float deltatime) { } if (_visibleDirty) { _visibleDirty = false; - _model->setVisibleInScene(getVisible(), scene); + // don't show overlays in mirrors + _model->setVisibleInScene(getVisible(), scene, render::ItemKey::TAG_BITS_0); } if (_drawInFrontDirty) { _drawInFrontDirty = false; @@ -120,8 +121,10 @@ void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::Scene } void ModelOverlay::setVisible(bool visible) { - Overlay::setVisible(visible); - _visibleDirty = true; + if (visible != getVisible()) { + Overlay::setVisible(visible); + _visibleDirty = true; + } } void ModelOverlay::setDrawInFront(bool drawInFront) { diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 60ba90e568..08c776c426 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -106,7 +106,7 @@ private: bool _jointMappingCompleted { false }; QVector _jointMapping; // domain is index into model-joints, range is index into animation-joints - bool _visibleDirty { false }; + bool _visibleDirty { true }; bool _drawInFrontDirty { false }; bool _drawInHUDDirty { false }; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 449ac62998..f99ced0021 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -44,6 +44,13 @@ namespace render { } else { builder.withViewSpace(); } + + if (!overlay->getVisible()) { + builder.withInvisible(); + } + + builder.withTagBits(render::ItemKey::TAG_BITS_0); // Only draw overlays in main view + return builder.build(); } template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 500a24763d..86635cd3bf 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -50,7 +50,7 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { - return ItemKey::Builder::opaqueShape().withTypeMeta(); + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) { return static_pointer_cast(avatar)->getBounds(); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 90bb83a663..d35d5c5317 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -398,7 +398,7 @@ void HmdDisplayPlugin::HUDRenderer::updatePipeline() { auto vs = gpu::Shader::createVertex(std::string(hmd_ui_vert)); auto ps = gpu::Shader::createPixel(std::string(hmd_ui_frag)); auto program = gpu::Shader::createProgram(vs, ps); - gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet()); + gpu::Shader::makeProgram(*program, gpu::Shader::BindingSet()); uniformsLocation = program->getUniformBuffers().findLocation("hudBuffer"); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index fb9aba636b..aca2f4d35b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -159,10 +159,10 @@ Item::Bound EntityRenderer::getBound() { ItemKey EntityRenderer::getKey() { if (isTransparent()) { - return ItemKey::Builder::transparentShape().withTypeMeta(); + return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } - return ItemKey::Builder::opaqueShape().withTypeMeta(); + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -185,7 +185,12 @@ void EntityRenderer::render(RenderArgs* args) { emit requestRenderUpdate(); } - if (_visible) { + auto& renderMode = args->_renderMode; + bool cauterized = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && + renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE && + _cauterized); + + if (_visible && !cauterized) { doRender(args); } } @@ -366,6 +371,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa _moving = entity->isMovingRelativeToParent(); _visible = entity->getVisible(); + _cauterized = entity->getCauterized(); _needsRenderUpdate = false; }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 8eb82e2c6e..f8685df5da 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -124,6 +124,7 @@ protected: bool _isFading{ _entitiesShouldFadeFunction() }; bool _prevIsTransparent { false }; bool _visible { false }; + bool _cauterized { false }; bool _moving { false }; bool _needsRenderUpdate { false }; // Only touched on the rendering thread diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9fcb7640ef..137203f475 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1013,9 +1013,9 @@ ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Pare void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) { if (didVisualGeometryRequestSucceed) { - _itemKey = ItemKey::Builder().withTypeMeta(); + _itemKey = ItemKey::Builder().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } else { - _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape(); + _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } } @@ -1334,11 +1334,16 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); entity->stopModelOverrideIfNoParent(); - if (model->isVisible() != _visible) { + // Default behavior for model is to not be visible in main view if cauterized (aka parented to the avatar's neck joint) + uint32_t viewTaskBits = _cauterized ? + render::ItemKey::TAG_BITS_1 : // draw in every view except the main one (view zero) + render::ItemKey::TAG_BITS_ALL; // draw in all views + + if (model->isVisible() != _visible || model->getViewTagBits() != viewTaskBits) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(_visible, scene); + model->setVisibleInScene(_visible, scene, viewTaskBits); } // TODO? early exit here when not visible? diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 2059487426..af95878213 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -147,9 +147,9 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn ItemKey ParticleEffectEntityRenderer::getKey() { if (_visible) { - return ItemKey::Builder::transparentShape(); + return ItemKey::Builder::transparentShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } else { - return ItemKey::Builder().withInvisible().build(); + return ItemKey::Builder().withInvisible().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).build(); } } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 4d223669b4..42110170a0 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -112,7 +112,7 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) } ItemKey PolyLineEntityRenderer::getKey() { - return ItemKey::Builder::transparentShape().withTypeMeta(); + return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } ShapeKey PolyLineEntityRenderer::getShapeKey() { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index ade3790df6..abd14d017e 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -99,7 +99,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain. decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the - polyVox surface extractor to create _mesh (as well as set Simulation _dirtyFlags). Because Simulation::DIRTY_SHAPE + polyVox surface extractor to create _mesh (as well as set Simulation _flags). Because Simulation::DIRTY_SHAPE is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on the surface style. @@ -1138,7 +1138,7 @@ void RenderablePolyVoxEntityItem::setMesh(graphics::MeshPointer mesh) { bool neighborsNeedUpdate; withWriteLock([&] { if (!_collisionless) { - _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } _mesh = mesh; _meshDirty = true; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index b43944f26a..04f07c5bd3 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -269,7 +269,7 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe ItemKey ZoneEntityRenderer::getKey() { - return ItemKey::Builder().withTypeMeta().build(); + return ItemKey::Builder().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).build(); } bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index fe5213baa8..ed13a46414 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -933,7 +933,7 @@ void EntityItem::setDensity(float density) { withWriteLock([&] { if (_density != clampedDensity) { _density = clampedDensity; - _dirtyFlags |= Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_MASS; } }); } @@ -958,7 +958,7 @@ void EntityItem::setMass(float mass) { withWriteLock([&] { if (_density != newDensity) { _density = newDensity; - _dirtyFlags |= Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_MASS; } }); } @@ -1623,41 +1623,42 @@ void EntityItem::setParentID(const QUuid& value) { if (!value.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(value); if (entity) { - newParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING; + newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; } } if (!oldParentID.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID); if (entity) { - oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING; + oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; } } if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) { - newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING; + newParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; } if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) { - oldParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING; + oldParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; } if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { - if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) { - markDirtyFlags(Simulation::NO_BOOTSTRAPPING); - forEachDescendant([&](SpatiallyNestablePointer object) { - if (object->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(object); - entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP | Simulation::NO_BOOTSTRAPPING); - } - }); - } else { - clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { + markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + } + }); + } else { + clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); } }); } @@ -1694,7 +1695,7 @@ void EntityItem::setUnscaledDimensions(const glm::vec3& value) { locationChanged(); dimensionsChanged(); withWriteLock([&] { - _dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + _flags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); _queryAACubeSet = false; }); } @@ -1703,7 +1704,7 @@ void EntityItem::setUnscaledDimensions(const glm::vec3& value) { void EntityItem::setRotation(glm::quat rotation) { if (getLocalOrientation() != rotation) { setLocalOrientation(rotation); - _dirtyFlags |= Simulation::DIRTY_ROTATION; + _flags |= Simulation::DIRTY_ROTATION; forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); @@ -1733,7 +1734,7 @@ void EntityItem::setVelocity(const glm::vec3& value) { velocity = value; } setLocalVelocity(velocity); - _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; + _flags |= Simulation::DIRTY_LINEAR_VELOCITY; } } } @@ -1744,7 +1745,7 @@ void EntityItem::setDamping(float value) { withWriteLock([&] { if (_damping != clampedDamping) { _damping = clampedDamping; - _dirtyFlags |= Simulation::DIRTY_MATERIAL; + _flags |= Simulation::DIRTY_MATERIAL; } }); } @@ -1763,7 +1764,7 @@ void EntityItem::setGravity(const glm::vec3& value) { } else { _gravity = value; } - _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; + _flags |= Simulation::DIRTY_LINEAR_VELOCITY; } } } @@ -1788,7 +1789,7 @@ void EntityItem::setAngularVelocity(const glm::vec3& value) { angularVelocity = value; } setLocalAngularVelocity(angularVelocity); - _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY; + _flags |= Simulation::DIRTY_ANGULAR_VELOCITY; } } } @@ -1799,7 +1800,7 @@ void EntityItem::setAngularDamping(float value) { withWriteLock([&] { if (_angularDamping != clampedDamping) { _angularDamping = clampedDamping; - _dirtyFlags |= Simulation::DIRTY_MATERIAL; + _flags |= Simulation::DIRTY_MATERIAL; } }); } @@ -1808,7 +1809,7 @@ void EntityItem::setCollisionless(bool value) { withWriteLock([&] { if (_collisionless != value) { _collisionless = value; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; + _flags |= Simulation::DIRTY_COLLISION_GROUP; } }); } @@ -1817,7 +1818,7 @@ void EntityItem::setCollisionMask(uint8_t value) { withWriteLock([&] { if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) { _collisionMask = (value & ENTITY_COLLISION_MASK_DEFAULT); - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; + _flags |= Simulation::DIRTY_COLLISION_GROUP; } }); } @@ -1829,11 +1830,11 @@ void EntityItem::setDynamic(bool value) { if (value && getShapeType() == SHAPE_TYPE_STATIC_MESH) { if (_dynamic) { _dynamic = false; - _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; + _flags |= Simulation::DIRTY_MOTION_TYPE; } } else { _dynamic = value; - _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; + _flags |= Simulation::DIRTY_MOTION_TYPE; } }); } @@ -1844,7 +1845,7 @@ void EntityItem::setRestitution(float value) { withWriteLock([&] { if (_restitution != clampedValue) { _restitution = clampedValue; - _dirtyFlags |= Simulation::DIRTY_MATERIAL; + _flags |= Simulation::DIRTY_MATERIAL; } }); @@ -1855,7 +1856,7 @@ void EntityItem::setFriction(float value) { withWriteLock([&] { if (_friction != clampedValue) { _friction = clampedValue; - _dirtyFlags |= Simulation::DIRTY_MATERIAL; + _flags |= Simulation::DIRTY_MATERIAL; } }); } @@ -1864,7 +1865,7 @@ void EntityItem::setLifetime(float value) { withWriteLock([&] { if (_lifetime != value) { _lifetime = value; - _dirtyFlags |= Simulation::DIRTY_LIFETIME; + _flags |= Simulation::DIRTY_LIFETIME; } }); } @@ -1873,7 +1874,7 @@ void EntityItem::setCreated(quint64 value) { withWriteLock([&] { if (_created != value) { _created = value; - _dirtyFlags |= Simulation::DIRTY_LIFETIME; + _flags |= Simulation::DIRTY_LIFETIME; } }); } @@ -1902,7 +1903,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); @@ -1997,17 +1998,18 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn serializeActions(success, newDataCache); if (success) { _allActionsDataCache = newDataCache; - _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + _flags |= Simulation::DIRTY_PHYSICS_ACTIVATION; auto actionType = action->getType(); if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { - if (!(bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { - _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { + _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); } }); } @@ -2033,7 +2035,7 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a if (success) { action->setIsMine(true); serializeActions(success, _allActionsDataCache); - _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + _flags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } else { qCDebug(entities) << "EntityItem::updateAction failed"; } @@ -2091,17 +2093,17 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi _objectActions.remove(actionID); if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) { - _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); } }); } else { - // NO-OP: we assume NO_BOOTSTRAPPING bits and collision group are correct + // NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct // because they should have been set correctly when the action was added // and/or when children were linked } @@ -2112,7 +2114,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi bool success = true; serializeActions(success, _allActionsDataCache); - _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + _flags |= Simulation::DIRTY_PHYSICS_ACTIVATION; setDynamicDataNeedsTransmit(true); return success; } @@ -2132,8 +2134,8 @@ bool EntityItem::clearActions(EntitySimulationPointer simulation) { // empty _serializedActions means no actions for the EntityItem _actionsToRemove.clear(); _allActionsDataCache.clear(); - _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + _flags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar }); return true; } @@ -2364,7 +2366,7 @@ QList EntityItem::getActionsOfType(EntityDynamicType typeT void EntityItem::locationChanged(bool tellPhysics) { requiresRecalcBoxes(); if (tellPhysics) { - _dirtyFlags |= Simulation::DIRTY_TRANSFORM; + _flags |= Simulation::DIRTY_TRANSFORM; EntityTreePointer tree = getTree(); if (tree) { tree->entityChanged(getThisPointer()); @@ -2832,7 +2834,7 @@ DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVer uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { - result = _dirtyFlags; + result = _flags & Simulation::DIRTY_FLAGS; }); return result; } @@ -2840,13 +2842,37 @@ uint32_t EntityItem::getDirtyFlags() const { void EntityItem::markDirtyFlags(uint32_t mask) { withWriteLock([&] { - _dirtyFlags |= mask; + mask &= Simulation::DIRTY_FLAGS; + _flags |= mask; }); } void EntityItem::clearDirtyFlags(uint32_t mask) { withWriteLock([&] { - _dirtyFlags &= ~mask; + mask &= Simulation::DIRTY_FLAGS; + _flags &= ~mask; + }); +} + +uint32_t EntityItem::getSpecialFlags() const { + uint32_t result; + withReadLock([&] { + result = _flags & Simulation::SPECIAL_FLAGS; + }); + return result; +} + +void EntityItem::markSpecialFlags(uint32_t mask) { + withWriteLock([&] { + mask &= Simulation::SPECIAL_FLAGS; + _flags |= mask; + }); +} + +void EntityItem::clearSpecialFlags(uint32_t mask) { + withWriteLock([&] { + mask &= Simulation::SPECIAL_FLAGS; + _flags &= ~mask; }); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4c398b8a29..5f84bcc311 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -358,7 +358,11 @@ public: uint32_t getDirtyFlags() const; void markDirtyFlags(uint32_t mask); - void clearDirtyFlags(uint32_t mask = 0xffffffff); + void clearDirtyFlags(uint32_t mask = 0x0000ffff); + + uint32_t getSpecialFlags() const; + void markSpecialFlags(uint32_t mask); + void clearSpecialFlags(uint32_t mask = 0xffff0000); bool isMoving() const; bool isMovingRelativeToParent() const; @@ -385,7 +389,7 @@ public: void getAllTerseUpdateProperties(EntityItemProperties& properties) const; void flagForOwnershipBid(uint8_t priority); - void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } + void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; } QString actionsToDebugString(); bool addAction(EntitySimulationPointer simulation, EntityDynamicPointer action); @@ -470,6 +474,9 @@ public: static QString _marketplacePublicKey; static void retrieveMarketplacePublicKey(); + void setCauterized(bool value) { _cauterized = value; } + bool getCauterized() const { return _cauterized; } + signals: void requestRenderUpdate(); @@ -572,7 +579,7 @@ protected: // // DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about. - uint32_t _dirtyFlags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation + uint32_t _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation // these backpointers are only ever set/cleared by friends: EntityTreeElementPointer _element; // set by EntityTreeElement @@ -623,6 +630,7 @@ protected: quint64 _lastUpdatedAccelerationTimestamp { 0 }; quint64 _lastUpdatedQueryAACubeTimestamp { 0 }; + bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index a615beefa9..5d33e4c047 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -85,7 +85,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChangedInAnimations = _animationProperties.setProperties(properties); if (somethingChangedInAnimations) { - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; + _flags |= Simulation::DIRTY_UPDATEABLE; } somethingChanged = somethingChanged || somethingChangedInAnimations; @@ -132,7 +132,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); if (animationPropertiesChanged) { - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; + _flags |= Simulation::DIRTY_UPDATEABLE; somethingChanged = true; } @@ -305,10 +305,10 @@ void ModelEntityItem::setShapeType(ShapeType type) { // dynamic and STATIC_MESH are incompatible // since the shape is being set here we clear the dynamic bit _dynamic = false; - _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; + _flags |= Simulation::DIRTY_MOTION_TYPE; } _shapeType = type; - _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } }); } @@ -336,7 +336,7 @@ void ModelEntityItem::setModelURL(const QString& url) { if (_modelURL != url) { _modelURL = url; if (_shapeType == SHAPE_TYPE_STATIC_MESH) { - _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } } }); @@ -348,14 +348,14 @@ void ModelEntityItem::setCompoundShapeURL(const QString& url) { ShapeType oldType = computeTrueShapeType(); _compoundShapeURL.set(url); if (oldType != computeTrueShapeType()) { - _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } } }); } void ModelEntityItem::setAnimationURL(const QString& url) { - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; + _flags |= Simulation::DIRTY_UPDATEABLE; withWriteLock([&] { _animationProperties.setURL(url); }); @@ -422,16 +422,16 @@ void ModelEntityItem::setAnimationSettings(const QString& value) { bool allowTranslation = settingsMap["allowTranslation"].toBool(); setAnimationAllowTranslation(allowTranslation); } - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; + _flags |= Simulation::DIRTY_UPDATEABLE; } void ModelEntityItem::setAnimationIsPlaying(bool value) { - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; + _flags |= Simulation::DIRTY_UPDATEABLE; _animationProperties.setRunning(value); } void ModelEntityItem::setAnimationFPS(float value) { - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; + _flags |= Simulation::DIRTY_UPDATEABLE; _animationProperties.setFPS(value); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index c20572a491..7d27011c56 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -601,7 +601,7 @@ void ParticleEffectEntityItem::setShapeType(ShapeType type) { withWriteLock([&] { if (type != _shapeType) { _shapeType = type; - _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } }); } diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h index aaa92000e7..c45b333b29 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/entities/src/SimulationFlags.h @@ -27,10 +27,28 @@ namespace Simulation { const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed - const uint32_t NO_BOOTSTRAPPING = 0x4000; + + // bits 17-32 are reservied for special flags + const uint32_t SPECIAL_FLAGS_NO_BOOTSTRAPPING = 0x10000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; + const uint32_t SPECIAL_FLAGS = SPECIAL_FLAGS_NO_BOOTSTRAPPING; + + const uint32_t DIRTY_FLAGS = DIRTY_POSITION | + DIRTY_ROTATION | + DIRTY_LINEAR_VELOCITY | + DIRTY_ANGULAR_VELOCITY | + DIRTY_MASS | + DIRTY_COLLISION_GROUP | + DIRTY_MOTION_TYPE | + DIRTY_SHAPE | + DIRTY_LIFETIME | + DIRTY_UPDATEABLE | + DIRTY_MATERIAL | + DIRTY_PHYSICS_ACTIVATION | + DIRTY_SIMULATOR_ID | + DIRTY_SIMULATION_OWNERSHIP_PRIORITY; }; #endif // hifi_SimulationFlags_h diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index 017c92b71c..ecd6fe3323 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -6,9 +6,9 @@ namespace gl { #ifdef SEPARATE_PROGRAM - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& error) { + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message) { #else - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& error) { + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message) { #endif if (shaderSource.empty()) { qCDebug(glLogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; @@ -34,52 +34,57 @@ namespace gl { GLint compiled = 0; glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); - // if compilation fails - if (!compiled) { - - // save the source code to a temp file so we can debug easily - /* - std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << srcstr[0]; - filestream << srcstr[1]; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + GLint infoLength = 0; + glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + if ((infoLength > 0) || !compiled) { char* temp = new char[infoLength]; glGetShaderInfoLog(glshader, infoLength, NULL, temp); + message = std::string(temp); - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << std::string(temp); - filestream.close(); - } - */ - - qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; - int lineNumber = 0; - for (auto s : srcstr) { - QString str(s); - QStringList lines = str.split("\n"); - for (auto& line : lines) { - qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + // if compilation fails + if (!compiled) { + // save the source code to a temp file so we can debug easily + /* + std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << srcstr[0]; + filestream << srcstr[1]; + filestream.close(); } + */ + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); + filestream.close(); + } + */ + + qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + int lineNumber = 0; + for (auto s : srcstr) { + QString str(s); + QStringList lines = str.split("\n"); + for (auto& line : lines) { + qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + } + } + qCCritical(glLogging) << "GLShader::compileShader - errors:"; + qCCritical(glLogging) << temp; + + delete[] temp; + glDeleteShader(glshader); + return false; } - qCCritical(glLogging) << "GLShader::compileShader - errors:"; - qCCritical(glLogging) << temp; - error = std::string(temp); + // Compilation success + qCWarning(glLogging) << "GLShader::compileShader - Success:"; + qCWarning(glLogging) << temp; delete[] temp; - - glDeleteShader(glshader); - return false; } #ifdef SEPARATE_PROGRAM @@ -137,7 +142,7 @@ namespace gl { return true; } -GLuint compileProgram(const std::vector& glshaders, std::string& error) { +GLuint compileProgram(const std::vector& glshaders, std::string& message, std::vector& binary) { // A brand new program: GLuint glprogram = glCreateProgram(); if (!glprogram) { @@ -157,39 +162,65 @@ GLuint compileProgram(const std::vector& glshaders, std::string& error) GLint linked = 0; glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - /* - // save the source code to a temp file so we can debug easily - std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << shaderSource->source; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + GLint infoLength = 0; + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + if ((infoLength > 0) || !linked) { char* temp = new char[infoLength]; glGetProgramInfoLog(glprogram, infoLength, NULL, temp); - qCDebug(glLogging) << "GLShader::compileProgram - failed to LINK the gl program object :"; - qCDebug(glLogging) << temp; + message = std::string(temp); - error = std::string(temp); - delete[] temp; + if (!linked) { + /* + // save the source code to a temp file so we can debug easily + std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << shaderSource->source; + filestream.close(); + } + */ - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << std::string(temp); - filestream.close(); + qCDebug(glLogging) << "GLShader::compileProgram - failed to LINK the gl program object :"; + qCDebug(glLogging) << temp; + + delete[] temp; + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); + filestream.close(); + } + */ + + glDeleteProgram(glprogram); + return 0; + } else { + qCDebug(glLogging) << "GLShader::compileProgram - success:"; + qCDebug(glLogging) << temp; + delete[] temp; } - */ + } - glDeleteProgram(glprogram); - return 0; + // If linked get the binaries + if (linked) { + GLint binaryLength = 0; + glGetProgramiv(glprogram, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + + if (binaryLength > 0) { + GLint numBinFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinFormats); + if (numBinFormats > 0) { + binary.resize(binaryLength); + std::vector binFormats(numBinFormats); + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, binFormats.data()); + + GLenum programBinFormat; + glGetProgramBinary(glprogram, binaryLength, NULL, &programBinFormat, binary.data()); + } + } } return glprogram; diff --git a/libraries/gl/src/gl/GLShaders.h b/libraries/gl/src/gl/GLShaders.h index a6213fd280..fc070d7659 100644 --- a/libraries/gl/src/gl/GLShaders.h +++ b/libraries/gl/src/gl/GLShaders.h @@ -17,12 +17,12 @@ namespace gl { #ifdef SEPARATE_PROGRAM - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& error); + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message); #else - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& error); + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message); #endif - GLuint compileProgram(const std::vector& glshaders, std::string& error); + GLuint compileProgram(const std::vector& glshaders, std::string& message, std::vector& binary); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index eb6de5df13..08bd20be66 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -68,8 +68,8 @@ GLBackend& getBackend() { return *INSTANCE; } -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { - return GLShader::makeProgram(getBackend(), shader, slotBindings); +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); } GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 5558d3ada1..18916ac18c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -64,7 +64,7 @@ protected: explicit GLBackend(bool syncCache); GLBackend(); public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler); virtual ~GLBackend(); @@ -423,8 +423,8 @@ protected: } _pipeline; // Backend dependant compilation of the shader - virtual GLShader* compileBackendProgram(const Shader& program); - virtual GLShader* compileBackendShader(const Shader& shader); + virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler); + virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler); virtual std::string getBackendShaderHeader() const; virtual void makeProgramBindings(ShaderObject& shaderObject); class ElementResource { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp index 9adfd550ef..93c9b0d2ff 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp @@ -56,28 +56,47 @@ static const std::array VERSION_DEFINES { { stereoVersion } }; -GLShader* GLBackend::compileBackendShader(const Shader& shader) { +GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; GLShader::ShaderObjects shaderObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + shader.incrementCompilationAttempt(); for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; - std::string error; + if (handler) { + bool retest = true; + std::string currentSrc = shaderSource; + // When a Handler is specified, we can try multiple times to build the shader and let the handler change the source if the compilation fails. + // The retest bool is set to false as soon as the compilation succeed to wexit the while loop. + // The handler tells us if we should retry or not while returning a modified version of the source. + while (retest) { + bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + compilationLogs[version].compiled = result; + if (!result) { + std::string newSrc; + retest = handler(shader, currentSrc, compilationLogs[version], newSrc); + currentSrc = newSrc; + } else { + retest = false; + } + } + } else { + compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + } -#ifdef SEPARATE_PROGRAM - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error); -#else - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); -#endif - if (!result) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + if (!compilationLogs[version].compiled) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); + shader.setCompilationLogs(compilationLogs); return nullptr; } } + // Compilation feedback + shader.setCompilationLogs(compilationLogs); // So far so good, the shader is created successfully GLShader* object = new GLShader(this->shared_from_this()); @@ -86,39 +105,47 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader) { return object; } -GLShader* GLBackend::compileBackendProgram(const Shader& program) { +GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler) { if (!program.isProgram()) { return nullptr; } GLShader::ShaderObjects programObjects; + program.incrementCompilationAttempt(); + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + for (int version = 0; version < GLShader::NumVersions; version++) { auto& programObject = programObjects[version]; // Let's go through every shaders and make sure they are ready to go std::vector< GLuint > shaderGLObjects; for (auto subShader : program.getShaders()) { - auto object = GLShader::sync((*this), *subShader); + auto object = GLShader::sync((*this), *subShader, handler); if (object) { shaderGLObjects.push_back(object->_shaderObjects[version].glshader); } else { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + compilationLogs[version].compiled = false; + compilationLogs[version].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?"); + program.setCompilationLogs(compilationLogs); return nullptr; } } - std::string error; - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str(); + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + program.setCompilationLogs(compilationLogs); return nullptr; } - + compilationLogs[version].compiled = true; programObject.glprogram = glprogram; makeProgramBindings(programObject); } + // Compilation feedback + program.setCompilationLogs(compilationLogs); // So far so good, the program versions have all been created successfully GLShader* object = new GLShader(this->shared_from_this()); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index 7ed9121978..010a7c479c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -30,7 +30,7 @@ GLShader::~GLShader() { } } -GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler) { GLShader* object = Backend::getGPUObject(shader); // If GPU object already created then good @@ -39,13 +39,13 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { } // need to have a gpu object? if (shader.isProgram()) { - GLShader* tempObject = backend.compileBackendProgram(shader); + GLShader* tempObject = backend.compileBackendProgram(shader, handler); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = backend.compileBackendShader(shader); + GLShader* tempObject = backend.compileBackendShader(shader, handler); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); @@ -56,10 +56,10 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { return object; } -bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { // First make sure the Shader has been compiled - GLShader* object = sync(backend, shader); + GLShader* object = sync(backend, shader, handler); if (!object) { return false; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl/src/gpu/gl/GLShader.h index dcf2dc330d..3259982e93 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.h @@ -21,8 +21,8 @@ struct ShaderObject { class GLShader : public GPUObject { public: - static GLShader* sync(GLBackend& backend, const Shader& shader); - static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); + static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler); enum Version { Mono = 0, diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp index 6fd5df6f81..fc1bc39929 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -61,8 +61,8 @@ GLBackend& getBackend() { return *INSTANCE; } -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { - return GLShader::makeProgram(getBackend(), shader, slotBindings); +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h index ea06b6b672..3681fc0492 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -61,7 +61,7 @@ protected: explicit GLBackend(bool syncCache); GLBackend(); public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet(), const Shader::CompilationHandler& handler = nullptr); virtual ~GLBackend(); @@ -420,8 +420,8 @@ protected: } _pipeline; // Backend dependant compilation of the shader - virtual GLShader* compileBackendProgram(const Shader& program); - virtual GLShader* compileBackendShader(const Shader& shader); + virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler); + virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler); virtual std::string getBackendShaderHeader() const; virtual void makeProgramBindings(ShaderObject& shaderObject); class ElementResource { diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp index fd44ad462f..677bba97ca 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp @@ -56,32 +56,50 @@ static const std::array VERSION_DEFINES { { stereoVersion } }; -GLShader* GLBackend::compileBackendShader(const Shader& shader) { +GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; GLShader::ShaderObjects shaderObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] - + "\n#extension GL_EXT_texture_buffer : enable" - + "\nprecision lowp float; // check precision 2" - + "\nprecision lowp samplerBuffer;" - + "\nprecision lowp sampler2DShadow;"; - std::string error; + + "\n#extension GL_EXT_texture_buffer : enable" + + "\nprecision lowp float; // check precision 2" + + "\nprecision lowp samplerBuffer;" + + "\nprecision lowp sampler2DShadow;"; + if (handler) { + bool retest = true; + std::string currentSrc = shaderSource; + // When a Handler is specified, we can try multiple times to build the shader and let the handler change the source if the compilation fails. + // The retest bool is set to false as soon as the compilation succeed to wexit the while loop. + // The handler tells us if we should retry or not while returning a modified version of the source. + while (retest) { + bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + compilationLogs[version].compiled = result; + if (!result) { + std::string newSrc; + retest = handler(shader, currentSrc, compilationLogs[version], newSrc); + currentSrc = newSrc; + } else { + retest = false; + } + } + } else { + compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + } -#ifdef SEPARATE_PROGRAM - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error); -#else - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); -#endif - if (!result) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + if (!compilationLogs[version].compiled) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); + shader.setCompilationLogs(compilationLogs); return nullptr; } } + // Compilation feedback + shader.setCompilationLogs(compilationLogs); // So far so good, the shader is created successfully GLShader* object = new GLShader(this->shared_from_this()); @@ -90,32 +108,35 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader) { return object; } -GLShader* GLBackend::compileBackendProgram(const Shader& program) { +GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler) { if (!program.isProgram()) { return nullptr; } GLShader::ShaderObjects programObjects; + Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + for (int version = 0; version < GLShader::NumVersions; version++) { auto& programObject = programObjects[version]; // Let's go through every shaders and make sure they are ready to go - std::vector shaderGLObjects; + std::vector< GLuint > shaderGLObjects; for (auto subShader : program.getShaders()) { - auto object = GLShader::sync((*this), *subShader); + auto object = GLShader::sync((*this), *subShader, handler); if (object) { shaderGLObjects.push_back(object->_shaderObjects[version].glshader); } else { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + program.setCompilationLogs(compilationLogs); return nullptr; } } - std::string error; - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str(); + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + program.setCompilationLogs(compilationLogs); return nullptr; } @@ -123,6 +144,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program) { makeProgramBindings(programObject); } + // Compilation feedback + program.setCompilationLogs(compilationLogs); // So far so good, the program versions have all been created successfully GLShader* object = new GLShader(this->shared_from_this()); diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp index 7ed9121978..010a7c479c 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp @@ -30,7 +30,7 @@ GLShader::~GLShader() { } } -GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler) { GLShader* object = Backend::getGPUObject(shader); // If GPU object already created then good @@ -39,13 +39,13 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { } // need to have a gpu object? if (shader.isProgram()) { - GLShader* tempObject = backend.compileBackendProgram(shader); + GLShader* tempObject = backend.compileBackendProgram(shader, handler); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = backend.compileBackendShader(shader); + GLShader* tempObject = backend.compileBackendShader(shader, handler); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); @@ -56,10 +56,10 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { return object; } -bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { // First make sure the Shader has been compiled - GLShader* object = sync(backend, shader); + GLShader* object = sync(backend, shader, handler); if (!object) { return false; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h index dcf2dc330d..f2a144a81c 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.h @@ -21,8 +21,8 @@ struct ShaderObject { class GLShader : public GPUObject { public: - static GLShader* sync(GLBackend& backend, const Shader& shader); - static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); + static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler = nullptr); enum Version { Mono = 0, diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 24128524da..d7d86c3ef7 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -127,7 +127,7 @@ void Context::executeFrame(const FramePointer& frame) const { _frameStats.evalDelta(beginStats, endStats); } -bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { +bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings, const Shader::CompilationHandler& handler) { // If we're running in another DLL context, we need to fetch the program callback out of the application // FIXME find a way to do this without reliance on Qt app properties if (!_makeProgramCallback) { @@ -135,7 +135,7 @@ bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { _makeProgramCallback = reinterpret_cast(rawCallback); } if (shader.isProgram() && _makeProgramCallback) { - return _makeProgramCallback(shader, bindings); + return _makeProgramCallback(shader, bindings, handler); } return false; } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 7b7575e9ed..195565f438 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -143,7 +143,7 @@ class Context { public: using Size = Resource::Size; typedef BackendPointer (*CreateBackend)(); - typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings); + typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings, const Shader::CompilationHandler& handler); // This one call must happen before any context is created or used (Shader::MakeProgram) in order to setup the Backend and any singleton data needed @@ -262,7 +262,7 @@ protected: // makeProgramShader(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings, const Shader::CompilationHandler& handler); static CreateBackend _createBackendCallback; static MakeProgram _makeProgramCallback; diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 398a269f3f..aa7898569b 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -17,59 +17,111 @@ using namespace gpu; -Shader::Shader(Type type, const Source& source): +std::atomic Shader::_nextShaderID( 1 ); +Shader::DomainShaderMaps Shader::_domainShaderMaps; +Shader::ProgramMap Shader::_programMap; + + +Shader::Shader(Type type, const Source& source) : _source(source), - _type(type) + _type(type), + _ID(_nextShaderID++) { } -Shader::Shader(Type type, const Pointer& vertex, const Pointer& pixel): - _type(type) +Shader::Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel): + _type(type), + _ID(_nextShaderID++) { - _shaders.resize(2); - _shaders[VERTEX] = vertex; - _shaders[PIXEL] = pixel; -} - -Shader::Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel) : -_type(type) { - _shaders.resize(3); - _shaders[VERTEX] = vertex; - _shaders[GEOMETRY] = geometry; - _shaders[PIXEL] = pixel; + if (geometry) { + _shaders.resize(3); + _shaders[VERTEX] = vertex; + _shaders[GEOMETRY] = geometry; + _shaders[PIXEL] = pixel; + } else { + _shaders.resize(2); + _shaders[VERTEX] = vertex; + _shaders[PIXEL] = pixel; + } } Shader::~Shader() { } +Shader::Pointer Shader::createOrReuseDomainShader(Type type, const Source& source) { + auto found = _domainShaderMaps[type].find(source); + if (found != _domainShaderMaps[type].end()) { + auto sharedShader = (*found).second.lock(); + if (sharedShader) { + return sharedShader; + } + } + auto shader = Pointer(new Shader(type, source)); + _domainShaderMaps[type].emplace(source, std::weak_ptr(shader)); + return shader; +} + Shader::Pointer Shader::createVertex(const Source& source) { - return Pointer(new Shader(VERTEX, source)); + return createOrReuseDomainShader(VERTEX, source); } Shader::Pointer Shader::createPixel(const Source& source) { - return Pointer(new Shader(PIXEL, source)); + return createOrReuseDomainShader(PIXEL, source); } Shader::Pointer Shader::createGeometry(const Source& source) { - return Pointer(new Shader(GEOMETRY, source)); + return createOrReuseDomainShader(GEOMETRY, source); } -Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { - if (vertexShader && vertexShader->getType() == VERTEX && - pixelShader && pixelShader->getType() == PIXEL) { - return Pointer(new Shader(PROGRAM, vertexShader, pixelShader)); +ShaderPointer Shader::createOrReuseProgramShader(Type type, const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader) { + ProgramMapKey key(0); + + if (vertexShader && vertexShader->getType() == VERTEX) { + key.x = vertexShader->getID(); + } else { + // Shader is not valid, exit + return Pointer(); } - return Pointer(); + + if (pixelShader && pixelShader->getType() == PIXEL) { + key.y = pixelShader->getID(); + } else { + // Shader is not valid, exit + return Pointer(); + } + + if (geometryShader) { + if (geometryShader->getType() == GEOMETRY) { + key.z = geometryShader->getID(); + } else { + // Shader is not valid, exit + return Pointer(); + } + } + + // program key is defined, now try to reuse + auto found = _programMap.find(key); + if (found != _programMap.end()) { + auto sharedShader = (*found).second.lock(); + if (sharedShader) { + return sharedShader; + } + } + + // Program is a new one, let's create it + auto program = Pointer(new Shader(type, vertexShader, geometryShader, pixelShader)); + _programMap.emplace(key, std::weak_ptr(program)); + return program; +} + + +Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { + return createOrReuseProgramShader(PROGRAM, vertexShader, nullptr, pixelShader); } Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader) { - if (vertexShader && vertexShader->getType() == VERTEX && - geometryShader && geometryShader->getType() == GEOMETRY && - pixelShader && pixelShader->getType() == PIXEL) { - return Pointer(new Shader(PROGRAM, vertexShader, geometryShader, pixelShader)); - } - return Pointer(); + return createOrReuseProgramShader(PROGRAM, vertexShader, geometryShader, pixelShader); } void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& uniformBuffers, const SlotSet& resourceBuffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs) { @@ -82,9 +134,21 @@ void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& uniformBuffers, _outputs = outputs; } -bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { +bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings, const CompilationHandler& handler) { if (shader.isProgram()) { - return Context::makeProgram(shader, bindings); + return Context::makeProgram(shader, bindings, handler); } return false; } + +void Shader::setCompilationLogs(const CompilationLogs& logs) const { + _compilationLogs.clear(); + for (const auto& log : logs) { + _compilationLogs.emplace_back(CompilationLog(log)); + } +} + +void Shader::incrementCompilationAttempt() const { + _numCompilationAttempts++; +} + diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 181c9b5e78..4504337789 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -22,6 +23,8 @@ namespace gpu { class Shader { public: + // unique identifier of a shader + using ID = uint32_t; typedef std::shared_ptr< Shader > Pointer; typedef std::vector< Pointer > Shaders; @@ -39,11 +42,29 @@ public: virtual const std::string& getCode() const { return _code; } + class Less { + public: + bool operator() (const Source& x, const Source& y) const { if (x._lang == y._lang) { return x._code < y._code; } else { return (x._lang < y._lang); } } + }; + protected: std::string _code; Language _lang = GLSL; }; + struct CompilationLog { + std::string message; + std::vector binary; + bool compiled{ false }; + + CompilationLog() {} + CompilationLog(const CompilationLog& src) : + message(src.message), + binary(src.binary), + compiled(src.compiled) {} + }; + using CompilationLogs = std::vector; + static const int32 INVALID_LOCATION = -1; class Slot { @@ -121,13 +142,12 @@ public: ~Shader(); + ID getID() const { return _ID; } + Type getType() const { return _type; } bool isProgram() const { return getType() > NUM_DOMAINS; } bool isDomain() const { return getType() < NUM_DOMAINS; } - void setCompilationHasFailed(bool compilationHasFailed) { _compilationHasFailed = compilationHasFailed; } - bool compilationHasFailed() const { return _compilationHasFailed; } - const Source& getSource() const { return _source; } const Shaders& getShaders() const { return _shaders; } @@ -155,6 +175,15 @@ public: const SlotSet& inputs, const SlotSet& outputs); + // Compilation Handler can be passed while compiling a shader (in the makeProgram call) to be able to give the hand to + // the caller thread if the comilation fails and to prvide a different version of the source for it + // @param0 the Shader object that just failed to compile + // @param1 the original source code as submited to the compiler + // @param2 the compilation log containing the error message + // @param3 a new string ready to be filled with the new version of the source that could be proposed from the handler functor + // @return boolean true if the backend should keep trying to compile the shader with the new source returned or false to stop and fail that shader compilation + using CompilationHandler = std::function; + // makeProgram(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. @@ -168,18 +197,29 @@ public: // on a gl Context and the driver to compile the glsl shader. // Hoppefully in a few years the shader compilation will be completely abstracted in a separate shader compiler library // independant of the graphics api in use underneath (looking at you opengl & vulkan). - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet(), const CompilationHandler& handler = nullptr); + + // Check the compilation state + bool compilationHasFailed() const { return _compilationHasFailed; } + const CompilationLogs& getCompilationLogs() const { return _compilationLogs; } + uint32_t getNumCompilationAttempts() const { return _numCompilationAttempts; } + + // Set COmpilation logs can only be called by the Backend layers + void setCompilationHasFailed(bool compilationHasFailed) { _compilationHasFailed = compilationHasFailed; } + void setCompilationLogs(const CompilationLogs& logs) const; + void incrementCompilationAttempt() const; + const GPUObjectPointer gpuObject {}; - + protected: Shader(Type type, const Source& source); - Shader(Type type, const Pointer& vertex, const Pointer& pixel); Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel); Shader(const Shader& shader); // deep copy of the sysmem shader Shader& operator=(const Shader& shader); // deep copy of the sysmem texture + // Source contains the actual source code or nothing if the shader is a program Source _source; @@ -198,8 +238,49 @@ protected: // The type of the shader, the master key Type _type; + // The unique identifier of a shader in the GPU lib + uint32_t _ID{ 0 }; + + // Number of attempts to compile the shader + mutable uint32_t _numCompilationAttempts{ 0 }; + // Compilation logs (one for each versions generated) + mutable CompilationLogs _compilationLogs; + // Whether or not the shader compilation failed bool _compilationHasFailed { false }; + + + // Global maps of the shaders + // Unique shader ID + static std::atomic _nextShaderID; + + using ShaderMap = std::map, Source::Less>; + using DomainShaderMaps = std::array; + static DomainShaderMaps _domainShaderMaps; + + static ShaderPointer createOrReuseDomainShader(Type type, const Source& source); + + using ProgramMapKey = glm::uvec3; // The IDs of the shaders in a program make its key + class ProgramKeyLess { + public: + bool operator() (const ProgramMapKey& l, const ProgramMapKey& r) const { + if (l.x == r.x) { + if (l.y == r.y) { + return (l.z < r.z); + } + else { + return (l.y < r.y); + } + } + else { + return (l.x < r.x); + } + } + }; + using ProgramMap = std::map, ProgramKeyLess>; + static ProgramMap _programMap; + + static ShaderPointer createOrReuseProgramShader(Type type, const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader); }; typedef Shader::Pointer ShaderPointer; diff --git a/libraries/gpu/src/gpu/null/NullBackend.h b/libraries/gpu/src/gpu/null/NullBackend.h index c9d249aec7..57b8fbafbc 100644 --- a/libraries/gpu/src/gpu/null/NullBackend.h +++ b/libraries/gpu/src/gpu/null/NullBackend.h @@ -28,7 +28,7 @@ class Backend : public gpu::Backend { friend class gpu::Context; static void init() {} static gpu::Backend* createBackend() { return new Backend(); } - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { return true; } + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { return true; } protected: explicit Backend(bool syncCache) : Parent() { } diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index c22e99cbbc..4b5b06ab0b 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -67,7 +67,7 @@ public: typedef render::Payload AnimDebugDrawPayload; namespace render { - template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return (data->_isVisible ? ItemKey::Builder::opaqueShape() : ItemKey::Builder::opaqueShape().withInvisible()); } + template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return (data->_isVisible ? ItemKey::Builder::opaqueShape() : ItemKey::Builder::opaqueShape().withInvisible()).withTagBits(ItemKey::TAG_BITS_ALL); } template <> const Item::Bound payloadGetBound(const AnimDebugDrawData::Pointer& data) { return data->_bound; } template <> void payloadRender(const AnimDebugDrawData::Pointer& data, RenderArgs* args) { data->render(args); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index efca0c3267..54dfd96a00 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -249,7 +249,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::TAG_BITS_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index fae645fdbc..51884ccbee 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -203,6 +203,12 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl return color; } +<@endfunc@> + +<@func declareEvalGlobalLightingAlphaBlendedWithHaze()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> vec3 evalGlobalLightingAlphaBlendedWithHaze( mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index acd30d527d..89d3f4faee 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -46,7 +46,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { float levels = getLightAmbientMapNumMips(ambient); float m = 12.0 / (1.0+11.0*surface.roughness); float lod = levels - m; - lod = max(lod, 0); + lod = max(lod, 0.0); specularLight = evalSkyboxLight(lightDir, lod).xyz; } <@endif@> diff --git a/libraries/render-utils/src/LightPayload.cpp b/libraries/render-utils/src/LightPayload.cpp index 09334faa13..79e0c1d94d 100644 --- a/libraries/render-utils/src/LightPayload.cpp +++ b/libraries/render-utils/src/LightPayload.cpp @@ -18,9 +18,13 @@ namespace render { template <> const ItemKey payloadGetKey(const LightPayload::Pointer& payload) { ItemKey::Builder builder; builder.withTypeLight(); - if (!payload || !payload->isVisible()) { - builder.withInvisible(); + builder.withTagBits(ItemKey::TAG_BITS_ALL); + if (payload) { + if (!payload->isVisible()) { + builder.withInvisible(); + } } + return builder.build(); } @@ -87,6 +91,7 @@ namespace render { template <> const ItemKey payloadGetKey(const KeyLightPayload::Pointer& payload) { ItemKey::Builder builder; builder.withTypeLight(); + builder.withTagBits(ItemKey::TAG_BITS_ALL); if (!payload || !payload->isVisible()) { builder.withInvisible(); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c506887fc4..9655b60a78 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -71,10 +71,20 @@ void MeshPartPayload::updateMaterial(graphics::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; } -ItemKey MeshPartPayload::getKey() const { +void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) { ItemKey::Builder builder; builder.withTypeShape(); + if (!isVisible) { + builder.withInvisible(); + } + + builder.withTagBits(tagBits); + + if (isLayered) { + builder.withLayered(); + } + if (_drawMaterial) { auto matKey = _drawMaterial->getKey(); if (matKey.isTranslucent()) { @@ -82,7 +92,11 @@ ItemKey MeshPartPayload::getKey() const { } } - return builder.build(); + _itemKey = builder.build(); +} + +ItemKey MeshPartPayload::getKey() const { + return _itemKey; } Item::Bound MeshPartPayload::getBound() const { @@ -389,7 +403,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render _worldBound.transform(boundTransform); } -void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered) { +void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) { ItemKey::Builder builder; builder.withTypeShape(); @@ -397,6 +411,8 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered) { builder.withInvisible(); } + builder.withTagBits(tagBits); + if (isLayered) { builder.withLayered(); } @@ -415,10 +431,6 @@ void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered) { _itemKey = builder.build(); } -ItemKey ModelMeshPartPayload::getKey() const { - return _itemKey; -} - void ModelMeshPartPayload::setLayer(bool isLayeredInFront, bool isLayeredInHUD) { if (isLayeredInFront) { _layer = Item::LAYER_3D_FRONT; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 8160b9f009..21f9dc2e68 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -33,6 +33,8 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; + virtual void updateKey(bool isVisible, bool isLayered, uint8_t tagBits); + virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); virtual void notifyLocationChanged() {} @@ -70,6 +72,9 @@ public: size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } int getMaterialTextureCount() { return _drawMaterial ? _drawMaterial->getTextureCount() : 0; } bool hasTextureInfo() const { return _drawMaterial ? _drawMaterial->hasTextureInfo() : false; } + +protected: + render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; }; namespace render { @@ -94,16 +99,15 @@ public: using TransformType = glm::mat4; #endif + void updateKey(bool isVisible, bool isLayered, uint8_t tagBits) override; void updateClusterBuffer(const std::vector& clusterTransforms); void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface - render::ItemKey getKey() const override; int getLayer() const; render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) override; - void setKey(bool isVisible, bool isLayered); void setLayer(bool isLayeredInFront, bool isLayeredInHUD); void setShapeKey(bool invalidateShapeKey, bool isWireframe); @@ -126,7 +130,6 @@ private: void initCache(const ModelPointer& model); gpu::BufferPointer _blendedVertexBuffer; - render::ItemKey _itemKey { render::ItemKey::Builder::opaqueShape().build() }; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; int _layer { render::Item::LAYER_3D }; }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b0763c0fb3..b9ccc28c01 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -268,6 +268,7 @@ void Model::updateRenderItems() { bool isWireframe = self->isWireframe(); bool isVisible = self->isVisible(); + uint8_t viewTagBits = self->getViewTagBits(); bool isLayeredInFront = self->isLayeredInFront(); bool isLayeredInHUD = self->isLayeredInHUD(); @@ -280,8 +281,10 @@ void Model::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey, - isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { + transaction.updateItem(itemID, [modelTransform, clusterTransforms, + invalidatePayloadShapeKey, isWireframe, isVisible, + viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { data.updateClusterBuffer(clusterTransforms); Transform renderTransform = modelTransform; @@ -297,7 +300,7 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); @@ -681,22 +684,25 @@ void Model::calculateTriangleSets() { } } -void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene) { - if (_isVisible != isVisible) { +void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits) { + if (_isVisible != isVisible || _viewTagBits != viewTagBits) { _isVisible = isVisible; + _viewTagBits = viewTagBits; bool isLayeredInFront = _isLayeredInFront; bool isLayeredInHUD = _isLayeredInHUD; render::Transaction transaction; foreach (auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); }); } scene->enqueueTransaction(transaction); @@ -709,18 +715,21 @@ void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& _isLayeredInFront = isLayeredInFront; bool isVisible = _isVisible; + uint8_t viewTagBits = _viewTagBits; bool isLayeredInHUD = _isLayeredInHUD; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } @@ -733,18 +742,21 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce _isLayeredInHUD = isLayeredInHUD; bool isVisible = _isVisible; + uint8_t viewTagBits = _viewTagBits; bool isLayeredInFront = _isLayeredInFront; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + isLayeredInHUD](ModelMeshPartPayload& data) { + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 027d52ecfd..ca0904f334 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,7 +86,7 @@ public: const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support - void setVisibleInScene(bool isVisible, const render::ScenePointer& scene); + void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits); void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene); void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene); bool needsFixupInScene() const; @@ -104,6 +104,7 @@ public: bool isRenderable() const; bool isVisible() const { return _isVisible; } + uint8_t getViewTagBits() const { return _viewTagBits; } bool isLayeredInFront() const { return _isLayeredInFront; } bool isLayeredInHUD() const { return _isLayeredInHUD; } @@ -396,6 +397,7 @@ protected: QUrl _url; bool _isVisible; + uint8_t _viewTagBits{ render::ItemKey::TAG_BITS_ALL }; gpu::Buffers _blendedVertexBuffers; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 1c828c3d06..eacd5b5b66 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -30,7 +30,7 @@ #include "model_lightmap_fade_vert.h" #include "model_lightmap_normal_map_fade_vert.h" #include "model_translucent_vert.h" -#include "model_translucent_fade_vert.h" +#include "model_translucent_normal_map_vert.h" #include "skin_model_fade_vert.h" #include "skin_model_normal_map_fade_vert.h" @@ -73,12 +73,14 @@ #include "model_lightmap_specular_map_frag.h" #include "model_translucent_frag.h" #include "model_translucent_unlit_frag.h" +#include "model_translucent_normal_map_frag.h" #include "model_lightmap_fade_frag.h" #include "model_lightmap_normal_map_fade_frag.h" #include "model_lightmap_normal_specular_map_fade_frag.h" #include "model_lightmap_specular_map_fade_frag.h" #include "model_translucent_fade_frag.h" +#include "model_translucent_normal_map_fade_frag.h" #include "model_translucent_unlit_fade_frag.h" #include "overlay3D_vert.h" @@ -191,7 +193,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); auto modelTranslucentVertex = gpu::Shader::createVertex(std::string(model_translucent_vert)); - auto modelTranslucentFadeVertex = gpu::Shader::createVertex(std::string(model_translucent_fade_vert)); + auto modelTranslucentNormalMapVertex = gpu::Shader::createVertex(std::string(model_translucent_normal_map_vert)); auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); @@ -220,6 +222,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); + auto modelTranslucentNormalMapPixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_frag)); auto modelTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_frag)); auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); @@ -238,6 +241,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_fade_frag)); auto modelShadowFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); auto modelTranslucentFadePixel = gpu::Shader::createPixel(std::string(model_translucent_fade_frag)); + auto modelTranslucentNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_fade_frag)); auto modelTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_fade_frag)); auto simpleFadePixel = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); auto simpleUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); @@ -307,13 +311,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleVertex, simpleTranslucentUnlitPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents(), - modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular(), modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular(), - modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap(), @@ -321,7 +325,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withTranslucent().withFade(), - modelTranslucentFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withTranslucent().withFade(), simpleFadeVertex, simpleTranslucentFadePixel, batchSetter, itemSetter); @@ -333,13 +337,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleFadeVertex, simpleTranslucentUnlitFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withFade(), - modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular().withFade(), modelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular().withFade(), - modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap().withFade(), @@ -405,26 +409,26 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents(), - skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular(), skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular(), - skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withFade(), skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withFade(), - skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular().withFade(), skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular().withFade(), - skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); // Depth-only addPipeline( diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d83dfd73a5..19a3f4ed73 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -200,7 +200,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con }); } -void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) { +void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; }; // Prepare the ShapePipeline @@ -216,7 +216,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende task.addJob("ShadowSetup"); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { - const auto setupOutput = task.addJob("ShadowCascadeSetup", i); + const auto setupOutput = task.addJob("ShadowCascadeSetup", i, tagBits, tagMask); const auto shadowFilter = setupOutput.getN(1); // CPU jobs: @@ -264,7 +264,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask); globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index d8d4c624e7..33e0ad4daa 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -48,7 +48,7 @@ public: using JobModel = render::Task::Model; RenderShadowTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); void configure(const Config& configuration); }; @@ -67,12 +67,14 @@ public: using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelO; - RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + RenderShadowCascadeSetup(unsigned int cascadeIndex, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : _cascadeIndex{ cascadeIndex }, _tagBits(tagBits), _tagMask(tagMask) {} void run(const render::RenderContextPointer& renderContext, Outputs& output); private: unsigned int _cascadeIndex; + uint8_t _tagBits{ 0x00 }; + uint8_t _tagMask{ 0x00 }; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index dc6c66e058..19924b4ddc 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -14,7 +14,7 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" -void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) { +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits, uint8_t tagMask) { // auto items = input.get(); // Shadows use an orthographic projection because they are linked to sunlights @@ -28,9 +28,9 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: const auto threshold = 1e-3f; return relativeBoundRadius > threshold; return true; - }); + }, tagBits, tagMask); - const auto items = task.addJob("FetchCullSort", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor, tagBits, tagMask); assert(items.canCast()); if (isDeferred) { diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index eb61f56eab..5da3d18474 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -23,7 +23,7 @@ public: RenderViewTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index bed85b4b15..b41e226e23 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -47,7 +47,7 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 761e702595..67a5651ab8 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index a2d271653c..316dae7aad 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> diff --git a/libraries/render-utils/src/model_translucent_normal_map.slf b/libraries/render-utils/src/model_translucent_normal_map.slf new file mode 100644 index 0000000000..759007d93e --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map.slf @@ -0,0 +1,91 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_translucent_normal_map.frag +// fragment shader +// +// Created by Olivier Prat on 23/01/2018. +// Copyright 2018 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 graphics/Material.slh@> + +<@include DeferredGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> + +<@include LightLocal.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec4 _worldPosition; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; +in float _alpha; + +out vec4 _fragColor; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + vec3 fragNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } + + _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + albedo, + fresnel, + metallic, + emissive, + surface, opacity, localLighting.rgb), + opacity); +} diff --git a/libraries/render-utils/src/model_translucent_fade.slv b/libraries/render-utils/src/model_translucent_normal_map.slv similarity index 88% rename from libraries/render-utils/src/model_translucent_fade.slv rename to libraries/render-utils/src/model_translucent_normal_map.slv index 3fb9ad2cb4..db824a3709 100644 --- a/libraries/render-utils/src/model_translucent_fade.slv +++ b/libraries/render-utils/src/model_translucent_normal_map.slv @@ -1,10 +1,10 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// model_translucent_fade.slv +// model_translucent_normal_map.slv // vertex shader // -// Created by Olivier Prat on 15/01/18. +// Created by Olivier Prat on 23/01/18. // Copyright 2018 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -25,6 +25,7 @@ out vec2 _texCoord1; out vec4 _position; out vec4 _worldPosition; out vec3 _normal; +out vec3 _tangent; out vec3 _color; void main(void) { @@ -41,4 +42,5 @@ void main(void) { <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> + <$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$> } diff --git a/libraries/render-utils/src/model_translucent_normal_map_fade.slf b/libraries/render-utils/src/model_translucent_normal_map_fade.slf new file mode 100644 index 0000000000..204b5ac56b --- /dev/null +++ b/libraries/render-utils/src/model_translucent_normal_map_fade.slf @@ -0,0 +1,101 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_translucent_normal_map_fade.frag +// fragment shader +// +// Created by Olivier Prat on 23/01/18. +// Copyright 2018 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 graphics/Material.slh@> + +<@include DeferredGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> + +<@include LightLocal.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +<@include Fade.slh@> +<$declareFadeFragment()$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; +in float _alpha; +in vec4 _worldPosition; + +out vec4 _fragColor; + +void main(void) { + vec3 fadeEmissive; + FadeObjectParams fadeParams; + + <$fetchFadeObjectParams(fadeParams)$> + applyFade(fadeParams, _worldPosition.xyz, fadeEmissive); + + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + // Lighting is done in world space + vec3 fragNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } + + _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + albedo, + fresnel, + metallic, + emissive+fadeEmissive, + surface, opacity, localLighting.rgb), + opacity); +} diff --git a/libraries/render-utils/src/overlay3D_model_translucent.slf b/libraries/render-utils/src/overlay3D_model_translucent.slf index 8dd3a81443..b66c114f4a 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent.slf @@ -11,7 +11,7 @@ // <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include graphics/Material.slh@> diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index b16b19c8b4..30c420233f 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -16,7 +16,7 @@ <@include DeferredBufferWrite.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> diff --git a/libraries/render-utils/src/simple_transparent_textured_fade.slf b/libraries/render-utils/src/simple_transparent_textured_fade.slf index ad260210a7..a8a5875a4b 100644 --- a/libraries/render-utils/src/simple_transparent_textured_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_fade.slf @@ -16,7 +16,7 @@ <@include DeferredBufferWrite.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 70331cdb47..8d99c3e8f2 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -61,7 +61,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc details._rendered += (int)outItems.size(); } -void FetchNonspatialItems::run(const RenderContextPointer& renderContext, ItemBounds& outItems) { +void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto& scene = renderContext->_scene; @@ -72,7 +72,9 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, ItemBo outItems.reserve(items.size()); for (auto& id : items) { auto& item = scene->getItem(id); - outItems.emplace_back(ItemBound(id, item.getBound())); + if (filter.test(item.getKey())) { + outItems.emplace_back(ItemBound(id, item.getBound())); + } } } diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 486c4f4cdf..4461537109 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -24,8 +24,8 @@ namespace render { class FetchNonspatialItems { public: - using JobModel = Job::ModelO; - void run(const RenderContextPointer& renderContext, ItemBounds& outItems); + using JobModel = Job::ModelIO; + void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemBounds& outItems); }; class FetchSpatialTreeConfig : public Job::Config { diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 036c7d3a99..612dba076b 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -34,6 +34,21 @@ const int Item::LAYER_3D = 1; const int Item::LAYER_3D_FRONT = 2; const int Item::LAYER_3D_HUD = 3; +const uint8_t ItemKey::TAG_BITS_ALL { 0xFF }; +const uint8_t ItemKey::TAG_BITS_NONE { 0x00 }; +const uint8_t ItemKey::TAG_BITS_0 { 0x01 }; +const uint8_t ItemKey::TAG_BITS_1 { 0x02 }; +const uint8_t ItemKey::TAG_BITS_2 { 0x04 }; +const uint8_t ItemKey::TAG_BITS_3 { 0x08 }; +const uint8_t ItemKey::TAG_BITS_4 { 0x10 }; +const uint8_t ItemKey::TAG_BITS_5 { 0x20 }; +const uint8_t ItemKey::TAG_BITS_6 { 0x40 }; +const uint8_t ItemKey::TAG_BITS_7 { 0x80 }; + +const uint32_t ItemKey::KEY_TAG_BITS_MASK = ((uint32_t) ItemKey::TAG_BITS_ALL) << FIRST_TAG_BIT; + + + void Item::Status::Value::setScale(float scale) { _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index e977c95fa0..ff4b3a0458 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -38,25 +38,62 @@ class Context; // Key is the KEY to filter Items and create specialized lists class ItemKey { public: - enum FlagBit { + // 8 tags are available to organize the items and filter them against as fields of the ItemKey. + // TAG & TAG_BITS are defined from several bits in the Key. + // An Item can be tagged and filtering can rely on the tags to keep or exclude items + // ItemKey are not taged by default + enum Tag : uint8_t { + TAG_0 = 0, // 8 Tags + TAG_1, + TAG_2, + TAG_3, + TAG_4, + TAG_5, + TAG_6, + TAG_7, + + NUM_TAGS + }; + // Tag bits are derived from the Tag enum + const static uint8_t TAG_BITS_ALL; + const static uint8_t TAG_BITS_NONE; + const static uint8_t TAG_BITS_0; + const static uint8_t TAG_BITS_1; + const static uint8_t TAG_BITS_2; + const static uint8_t TAG_BITS_3; + const static uint8_t TAG_BITS_4; + const static uint8_t TAG_BITS_5; + const static uint8_t TAG_BITS_6; + const static uint8_t TAG_BITS_7; + + enum FlagBit : uint32_t { TYPE_SHAPE = 0, // Item is a Shape TYPE_LIGHT, // Item is a Light TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items + TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item DEFORMED, // Deformed within bound, not solid - INVISIBLE, // Visible or not? could be just here to cast shadow + INVISIBLE, // Visible or not in the scene? SHADOW_CASTER, // Item cast shadows - PICKABLE, // Item can be picked/selected LAYERED, // Item belongs to one of the layers different from the default layer - SMALLER, + FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against + LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, + + __SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells) NUM_FLAGS, // Not a valid flag }; typedef std::bitset Flags; + // All the bits touching tag bits sets to true + const static uint32_t KEY_TAG_BITS_MASK; + static uint32_t evalTagBitsWithKeyBits(uint8_t tagBits, const uint32_t keyBits) { + return (keyBits & ~KEY_TAG_BITS_MASK) | (((uint32_t)tagBits) << FIRST_TAG_BIT); + } + // The key is the Flags Flags _flags; @@ -84,9 +121,12 @@ public: Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } - Builder& withPickable() { _flags.set(PICKABLE); return (*this); } Builder& withLayered() { _flags.set(LAYERED); return (*this); } + Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); } + // Set ALL the tags in one call using the Tag bits + Builder& withTagBits(uint8_t tagBits) { _flags = evalTagBitsWithKeyBits(tagBits, _flags.to_ulong()); return (*this); } + // Convenient standard keys that we will keep on using all over the place static Builder opaqueShape() { return Builder().withTypeShape(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } @@ -116,14 +156,15 @@ public: bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } - bool isPickable() const { return _flags[PICKABLE]; } - bool isLayered() const { return _flags[LAYERED]; } bool isSpatial() const { return !isLayered(); } + bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; } + uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } + // Probably not public, flags used by the scene - bool isSmall() const { return _flags[SMALLER]; } - void setSmaller(bool smaller) { (smaller ? _flags.set(SMALLER) : _flags.reset(SMALLER)); } + bool isSmall() const { return _flags[__SMALLER]; } + void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); } bool operator==(const ItemKey& key) { return (_flags == key._flags); } bool operator!=(const ItemKey& key) { return (_flags != key._flags); } @@ -177,11 +218,14 @@ public: Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } - Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } - Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } + Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } + // Set ALL the tags in one call using the Tag bits and the Tag bits touched + Builder& withTagBits(uint8_t tagBits, uint8_t tagMask) { _value = ItemKey::evalTagBitsWithKeyBits(tagBits, _value.to_ulong()); _mask = ItemKey::evalTagBitsWithKeyBits(tagMask, _mask.to_ulong()); return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index d7294fa2bd..7f60d5bb52 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -17,19 +17,21 @@ using namespace render; -void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor) { +void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varying& output, CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // CPU jobs: // Fetch and cull the items from the scene - const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered().withTagBits(tagBits, tagMask); const auto spatialFilter = render::Varying(filter); const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); // Overlays are not culled - const auto nonspatialSelection = task.addJob("FetchOverlaySelection"); + const ItemFilter overlayfilter = ItemFilter::Builder().withVisible().withTagBits(tagBits, tagMask); + const auto nonspatialFilter = render::Varying(overlayfilter); + const auto nonspatialSelection = task.addJob("FetchOverlaySelection", nonspatialFilter); // Multi filter visible items into different buckets const int NUM_SPATIAL_FILTERS = 4; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index b25480ae3a..8c9f2e7304 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -36,7 +36,7 @@ public: RenderFetchCullSortTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; #endif // hifi_RenderFetchCullSortTask_h diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 6d9d8230e4..f6ed00b493 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -73,8 +73,9 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p ShapeKey key{ filter._flags }; + // don't call makeProgram on shaders that are already made. - if (program->getUniformBuffers().empty()) { + if (program->getNumCompilationAttempts() < 1) { gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), Slot::BUFFER::LIGHTING_MODEL)); slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), Slot::BUFFER::SKINNING)); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 4403eaeff7..4b5f0e6517 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -200,9 +200,10 @@ public: std::string fsSource = HMD_REPROJECTION_FRAG; GLuint vertexShader { 0 }, fragmentShader { 0 }; std::string error; + std::vector binary; ::gl::compileShader(GL_VERTEX_SHADER, vsSource, "", vertexShader, error); ::gl::compileShader(GL_FRAGMENT_SHADER, fsSource, "", fragmentShader, error); - _program = ::gl::compileProgram({ { vertexShader, fragmentShader } }, error); + _program = ::gl::compileProgram({ { vertexShader, fragmentShader } }, error, binary); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); qDebug() << "Rebuild proigram"; diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 46672a3f66..cef61bdc53 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -527,6 +527,80 @@ // //*********************************************** + var sendMoneyRecipient; + var sendMoneyParticleEffectUpdateTimer; + var particleEffectTimestamp; + var sendMoneyParticleEffect; + var SEND_MONEY_PARTICLE_TIMER_UPDATE = 250; + var SEND_MONEY_PARTICLE_EMITTING_DURATION = 3000; + var SEND_MONEY_PARTICLE_LIFETIME_SECONDS = 8; + var SEND_MONEY_PARTICLE_PROPERTIES = { + accelerationSpread: { x: 0, y: 0, z: 0 }, + alpha: 1, + alphaFinish: 1, + alphaSpread: 0, + alphaStart: 1, + azimuthFinish: 0, + azimuthStart: -6, + color: { red: 143, green: 5, blue: 255 }, + colorFinish: { red: 255, green: 0, blue: 204 }, + colorSpread: { red: 0, green: 0, blue: 0 }, + colorStart: { red: 0, green: 136, blue: 255 }, + emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate + emitDimensions: { x: 0, y: 0, z: 0 }, + emitOrientation: { x: 0, y: 0, z: 0 }, + emitRate: 4, + emitSpeed: 2.1, + emitterShouldTrail: true, + isEmitting: 1, + lifespan: SEND_MONEY_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate + lifetime: SEND_MONEY_PARTICLE_LIFETIME_SECONDS + 1, + maxParticles: 20, + name: 'hfc-particles', + particleRadius: 0.2, + polarFinish: 0, + polarStart: 0, + radiusFinish: 0.05, + radiusSpread: 0, + radiusStart: 0.2, + speedSpread: 0, + textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", + type: 'ParticleEffect' + }; + + function updateSendMoneyParticleEffect() { + var timestampNow = Date.now(); + if ((timestampNow - particleEffectTimestamp) > (SEND_MONEY_PARTICLE_LIFETIME_SECONDS * 1000)) { + deleteSendMoneyParticleEffect(); + return; + } else if ((timestampNow - particleEffectTimestamp) > SEND_MONEY_PARTICLE_EMITTING_DURATION) { + Entities.editEntity(sendMoneyParticleEffect, { + isEmitting: 0 + }); + } else if (sendMoneyParticleEffect) { + var recipientPosition = AvatarList.getAvatar(sendMoneyRecipient).position; + var distance = Vec3.distance(recipientPosition, MyAvatar.position); + var accel = Vec3.subtract(recipientPosition, MyAvatar.position); + accel.y -= 3.0; + var life = Math.sqrt(2 * distance / Vec3.length(accel)); + Entities.editEntity(sendMoneyParticleEffect, { + emitAcceleration: accel, + lifespan: life + }); + } + } + + function deleteSendMoneyParticleEffect() { + if (sendMoneyParticleEffectUpdateTimer) { + Script.clearInterval(sendMoneyParticleEffectUpdateTimer); + sendMoneyParticleEffectUpdateTimer = null; + } + if (sendMoneyParticleEffect) { + sendMoneyParticleEffect = Entities.deleteEntity(sendMoneyParticleEffect); + } + sendMoneyRecipient = null; + } + // Function Name: fromQml() // // Description: @@ -534,6 +608,7 @@ // in the format "{method, params}", like json-rpc. See also sendToQml(). var isHmdPreviewDisabled = true; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); + function fromQml(message) { switch (message.method) { case 'passphrasePopup_cancelClicked': @@ -605,6 +680,19 @@ } removeOverlays(); break; + case 'sendMoney_sendPublicly': + deleteSendMoneyParticleEffect(); + sendMoneyRecipient = message.recipient; + var amount = message.amount; + var props = SEND_MONEY_PARTICLE_PROPERTIES; + props.parentID = MyAvatar.sessionUUID; + props.position = MyAvatar.position; + props.position.y += 0.2; + sendMoneyParticleEffect = Entities.addEntity(props, true); + particleEffectTimestamp = Date.now(); + updateSendMoneyParticleEffect(); + sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); + break; default: print('Unrecognized message from QML:', JSON.stringify(message)); } @@ -706,6 +794,7 @@ function shutdown() { button.clicked.disconnect(onButtonClicked); tablet.removeButton(button); + deleteSendMoneyParticleEffect(); if (tablet) { tablet.screenChanged.disconnect(onTabletScreenChanged); if (onWalletScreen) { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index ee3a9ce7ec..facb932eb0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -578,6 +578,7 @@ var selectionDisplay = null; // for gridTool.js to ignore case 'refreshConnections': case 'enable_ChooseRecipientNearbyMode': case 'disable_ChooseRecipientNearbyMode': + case 'sendMoney_sendPublicly': // NOP break; default: diff --git a/scripts/system/progress.js b/scripts/system/progress.js index f4741c5b6a..db09d40608 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -300,7 +300,7 @@ if (visible) { x = ((Date.now() / 1000) % ANIMATION_SECONDS_PER_REPEAT) / ANIMATION_SECONDS_PER_REPEAT; - if (isHMD) { + if (!isHMD) { x = x * barDesktop.repeat; } else { x = x * BAR_HMD_REPEAT; @@ -309,9 +309,9 @@ // Update progress bar Overlays.editOverlay(barDesktop.overlay, { visible: !isHMD, - subImage: { + bounds: { x: barDesktop.repeat - x, - y: 0, + y: windowHeight - barDesktop.height, width: barDesktop.width - barDesktop.repeat, height: barDesktop.height } @@ -319,9 +319,9 @@ Overlays.editOverlay(barHMD.overlay, { visible: isHMD, - subImage: { + bounds: { x: BAR_HMD_REPEAT - x, - y: 0, + y: windowHeight - BAR_HMD_HEIGHT, width: BAR_HMD_WIDTH - BAR_HMD_REPEAT, height: BAR_HMD_HEIGHT } diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index cd307ba362..3f48e37a76 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -137,12 +137,13 @@ const std::string PIXEL_SHADER_DEFINES{ R"GLSL( void testShaderBuild(const char* vs_src, const char * fs_src) { std::string error; + std::vector binary; GLuint vs, fs; if (!gl::compileShader(GL_VERTEX_SHADER, vs_src, VERTEX_SHADER_DEFINES, vs, error) || !gl::compileShader(GL_FRAGMENT_SHADER, fs_src, PIXEL_SHADER_DEFINES, fs, error)) { throw std::runtime_error("Failed to compile shader"); } - auto pr = gl::compileProgram({ vs, fs }, error); + auto pr = gl::compileProgram({ vs, fs }, error, binary); if (!pr) { throw std::runtime_error("Failed to link shader"); }