diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4f83e0f6e6..cd76704d34 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1124,19 +1124,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return qApp->isHMDMode() ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float { - return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0; + return qApp->getCamera().getMode() == CAMERA_MODE_MIRROR ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float { - return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0; + return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { - return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; + return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float { - return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0; + return qApp->getCamera().getMode() == CAMERA_MODE_ENTITY ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float { - return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0; + return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; @@ -1436,6 +1436,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo entityPacketSender->setMyAvatar(myAvatar.get()); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); + connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool))); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); auto textureCache = DependencyManager::get(); @@ -1785,14 +1786,23 @@ void Application::cleanupBeforeQuit() { DependencyManager::destroy(); DependencyManager::destroy(); - // shutdown render engine - _main3DScene = nullptr; - _renderEngine = nullptr; - qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } Application::~Application() { + // remove avatars from physics engine + DependencyManager::get()->clearOtherAvatars(); + VectorOfMotionStates motionStates; + DependencyManager::get()->getObjectsToRemoveFromPhysics(motionStates); + _physicsEngine->removeObjects(motionStates); + DependencyManager::get()->deleteAllAvatars(); + + _physicsEngine->setCharacterController(nullptr); + + // shutdown render engine + _main3DScene = nullptr; + _renderEngine = nullptr; + DependencyManager::destroy(); _entityClipboard->eraseAllOctreeElements(); @@ -1804,14 +1814,6 @@ Application::~Application() { _octreeProcessor.terminate(); _entityEditSender.terminate(); - _physicsEngine->setCharacterController(nullptr); - - // remove avatars from physics engine - DependencyManager::get()->clearOtherAvatars(); - VectorOfMotionStates motionStates; - DependencyManager::get()->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - DependencyManager::get()->deleteAllAvatars(); DependencyManager::destroy(); DependencyManager::destroy(); @@ -2083,7 +2085,7 @@ void Application::initializeUi() { void Application::paintGL() { // Some plugins process message events, allowing paintGL to be called reentrantly. - if (_inPaint || _aboutToQuit) { + if (_inPaint || _aboutToQuit || _window->isMinimized()) { return; } @@ -2440,7 +2442,7 @@ void Application::resizeGL() { // Possible change in aspect ratio { QMutexLocker viewLocker(&_viewMutex); - loadViewFrustum(_myCamera, _viewFrustum); + _myCamera.loadViewFrustum(_viewFrustum); } auto offscreenUi = DependencyManager::get(); @@ -4525,7 +4527,7 @@ void Application::update(float deltaTime) { // to the server. { QMutexLocker viewLocker(&_viewMutex); - loadViewFrustum(_myCamera, _viewFrustum); + _myCamera.loadViewFrustum(_viewFrustum); } quint64 now = usecTimestampNow(); @@ -4853,24 +4855,6 @@ QRect Application::getDesirableApplicationGeometry() const { return applicationGeometry; } -///////////////////////////////////////////////////////////////////////////////////// -// loadViewFrustum() -// -// Description: this will load the view frustum bounds for EITHER the head -// or the "myCamera". -// -void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { - // We will use these below, from either the camera or head vectors calculated above - viewFrustum.setProjection(camera.getProjection()); - - // Set the viewFrustum up with the correct position and orientation of the camera - viewFrustum.setPosition(camera.getPosition()); - viewFrustum.setOrientation(camera.getOrientation()); - - // Ask the ViewFrustum class to calculate our corners - viewFrustum.calculate(); -} - glm::vec3 Application::getSunDirection() const { // Sun direction is in fact just the location of the sun relative to the origin auto skyStage = DependencyManager::get()->getSkyStage(); @@ -5042,7 +5026,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // load the view frustum { QMutexLocker viewLocker(&_viewMutex); - loadViewFrustum(theCamera, _displayViewFrustum); + theCamera.loadViewFrustum(_displayViewFrustum); } // TODO fix shadows and make them use the GPU library @@ -5059,7 +5043,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se transaction.resetItem(BackgroundRenderData::_item, backgroundRenderPayload); } - // Assuming nothing get's rendered through that + // Assuming nothing gets rendered through that if (!selfAvatarOnly) { if (DependencyManager::get()->shouldRenderEntities()) { // render models... @@ -5115,6 +5099,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se QMutexLocker viewLocker(&_viewMutex); renderArgs->setViewFrustum(_displayViewFrustum); } + renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK + renderArgs->_scene = getMain3DScene(); _renderEngine->getRenderContext()->args = renderArgs; // Before the deferred pass, let's try to use the render engine @@ -6501,6 +6487,14 @@ void Application::activeChanged(Qt::ApplicationState state) { } } +void Application::windowMinimizedChanged(bool minimized) { + if (!minimized && !getActiveDisplayPlugin()->isActive()) { + getActiveDisplayPlugin()->activate(); + } else if (minimized && getActiveDisplayPlugin()->isActive()) { + getActiveDisplayPlugin()->deactivate(); + } +} + void Application::postLambdaEvent(std::function f) { if (this->thread() == QThread::currentThread()) { f(); diff --git a/interface/src/Application.h b/interface/src/Application.h index cf79a2dfd8..55c8303905 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -52,7 +52,7 @@ #include "avatar/MyAvatar.h" #include "BandwidthRecorder.h" -#include "Camera.h" +#include "FancyCamera.h" #include "ConnectionMonitor.h" #include "gpu/Context.h" #include "Menu.h" @@ -174,8 +174,8 @@ public: bool isThrottleRendering() const; - Camera* getCamera() { return &_myCamera; } - const Camera* getCamera() const { return &_myCamera; } + Camera& getCamera() { return _myCamera; } + const Camera& getCamera() const { return _myCamera; } // Represents the current view frustum of the avatar. void copyViewFrustum(ViewFrustum& viewOut) const; // Represents the view frustum of the current rendering pass, @@ -268,7 +268,7 @@ public: int getMaxOctreePacketsPerSecond() const; render::ScenePointer getMain3DScene() override { return _main3DScene; } - render::ScenePointer getMain3DScene() const { return _main3DScene; } + const render::ScenePointer& getMain3DScene() const { return _main3DScene; } render::EnginePointer getRenderEngine() override { return _renderEngine; } gpu::ContextPointer getGPUContext() const { return _gpuContext; } @@ -278,7 +278,7 @@ public: float getAvatarSimrate() const { return _avatarSimCounter.rate(); } float getAverageSimsPerSecond() const { return _simCounter.rate(); } - + void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f); void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); @@ -415,6 +415,7 @@ private slots: void faceTrackerMuteToggled(); void activeChanged(Qt::ApplicationState state); + void windowMinimizedChanged(bool minimized); void notifyPacketVersionMismatch(); @@ -462,7 +463,6 @@ private: void updateDialogs(float deltaTime) const; void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false); - static void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); glm::vec3 getSunDirection() const; @@ -558,7 +558,7 @@ private: SimpleMovingAverage _avatarSimsPerSecond {10}; int _avatarSimsPerSecondReport {0}; quint64 _lastAvatarSimsPerSecondUpdate {0}; - Camera _myCamera; // My view onto the world + FancyCamera _myCamera; // My view onto the world Setting::Handle _previousScriptLocation; Setting::Handle _fieldOfView; diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index cf3261ee88..37f53e3cc2 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -98,18 +98,7 @@ void Camera::setMode(CameraMode mode) { emit modeUpdated(modeToString(mode)); } -QUuid Camera::getCameraEntity() const { - if (_cameraEntity != nullptr) { - return _cameraEntity->getID(); - } - return QUuid(); -}; - -void Camera::setCameraEntity(QUuid entityID) { - _cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID); -} - -void Camera::setProjection(const glm::mat4& projection) { +void Camera::setProjection(const glm::mat4& projection) { _projection = projection; } @@ -119,7 +108,7 @@ PickRay Camera::computePickRay(float x, float y) { void Camera::setModeString(const QString& mode) { CameraMode targetMode = stringToMode(mode); - + switch (targetMode) { case CAMERA_MODE_FIRST_PERSON: Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); @@ -139,7 +128,7 @@ void Camera::setModeString(const QString& mode) { default: break; } - + qApp->cameraMenuChanged(); if (_mode != targetMode) { @@ -177,12 +166,6 @@ void Camera::loadViewFrustum(ViewFrustum& frustum) const { frustum.calculate(); } -ViewFrustum Camera::toViewFrustum() const { - ViewFrustum result; - loadViewFrustum(result); - return result; -} - QVariantMap Camera::getViewFrustum() { ViewFrustum frustum; loadViewFrustum(frustum); diff --git a/interface/src/Camera.h b/interface/src/Camera.h index cedfb3f185..c5a3b192ba 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -43,15 +43,11 @@ class Camera : public QObject { * @property position {Vec3} The position of the camera. * @property orientation {Quat} The orientation of the camera. * @property mode {string} The current camera mode. - * @property cameraEntity {EntityID} The position and rotation properties of - * the entity specified by this ID are then used as the camera's position and - * orientation. Only works when mode is "entity". * @property frustum {Object} The frustum of the camera. */ Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString) - Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT) public: @@ -65,9 +61,6 @@ public: void setMode(CameraMode m); void loadViewFrustum(ViewFrustum& frustum) const; - ViewFrustum toViewFrustum() const; - - EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; } const glm::mat4& getTransform() const { return _transform; } void setTransform(const glm::mat4& transform); @@ -87,9 +80,6 @@ public slots: glm::quat getOrientation() const { return _orientation; } void setOrientation(const glm::quat& orientation); - QUuid getCameraEntity() const; - void setCameraEntity(QUuid entityID); - /**jsdoc * Compute a {PickRay} based on the current camera configuration and the position x,y on the screen. * @function Camera.computePickRay @@ -143,7 +133,6 @@ private: glm::quat _orientation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; - EntityItemPointer _cameraEntity; }; #endif // hifi_Camera_h diff --git a/interface/src/FancyCamera.cpp b/interface/src/FancyCamera.cpp new file mode 100644 index 0000000000..7bb64a4a8e --- /dev/null +++ b/interface/src/FancyCamera.cpp @@ -0,0 +1,25 @@ +// +// FancyCamera.cpp +// interface/src +// +// Copyright 2017 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 "FancyCamera.h" + +#include "Application.h" + + +QUuid FancyCamera::getCameraEntity() const { + if (_cameraEntity != nullptr) { + return _cameraEntity->getID(); + } + return QUuid(); +}; + +void FancyCamera::setCameraEntity(QUuid entityID) { + _cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID); +} diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h new file mode 100644 index 0000000000..cd231cd929 --- /dev/null +++ b/interface/src/FancyCamera.h @@ -0,0 +1,43 @@ +// +// FancyCamera.h +// interface/src +// +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FancyCamera_h +#define hifi_FancyCamera_h + +#include "Camera.h" + +#include + +// TODO: come up with a better name than "FancyCamera" +class FancyCamera : public Camera { + Q_OBJECT + + /**jsdoc + * @namespace Camera + * @property cameraEntity {EntityID} The position and rotation properties of + * the entity specified by this ID are then used as the camera's position and + * orientation. Only works when mode is "entity". + */ + Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) + +public: + FancyCamera() : Camera() {} + + EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; } + +public slots: + QUuid getCameraEntity() const; + void setCameraEntity(QUuid entityID); + +private: + EntityItemPointer _cameraEntity; +}; + +#endif // hifi_FancyCamera_h diff --git a/interface/src/Physics.cpp b/interface/src/Physics.cpp deleted file mode 100644 index 7efa520571..0000000000 --- a/interface/src/Physics.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// Physics.cpp -// interface/src -// -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include - -#include "Util.h" -#include "world.h" -#include "Physics.h" - -// -// Applies static friction: maxVelocity is the largest velocity for which there -// there is friction, and strength is the amount of friction force applied to reduce -// velocity. -// -void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength) { - float v = glm::length(velocity); - if (v < maxVelocity) { - velocity *= glm::clamp((1.0f - deltaTime * strength * (1.0f - v / maxVelocity)), 0.0f, 1.0f); - } -} - -// -// Applies velocity damping, with a strength value for linear and squared velocity damping -// - -void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength) { - if (squaredStrength == 0.0f) { - velocity *= glm::clamp(1.0f - deltaTime * linearStrength, 0.0f, 1.0f); - } else { - velocity *= glm::clamp(1.0f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.0f, 1.0f); - } -} - -void applyDampedSpring(float deltaTime, glm::vec3& velocity, glm::vec3& position, glm::vec3& targetPosition, float k, float damping) { - -} diff --git a/interface/src/Physics.h b/interface/src/Physics.h deleted file mode 100644 index 97e873d920..0000000000 --- a/interface/src/Physics.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Physics.h -// interface/src -// -// Created by Philip on 4/25/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Physics_h -#define hifi_Physics_h - -void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength); -void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength); - -#endif // hifi_Physics_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b4d5fea360..5b996a3cdf 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -9,44 +9,32 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "Avatar.h" -#include -#include - -#include -#include -#include -#include +#include +#include #include #include -#include -#include +#include #include #include #include #include #include +#include #include #include -#include #include #include -#include "Application.h" -#include "Avatar.h" #include "AvatarManager.h" #include "AvatarMotionState.h" -#include "Head.h" +#include "Camera.h" #include "Menu.h" -#include "Physics.h" -#include "Util.h" -#include "world.h" #include "InterfaceLogging.h" #include "SceneScriptingInterface.h" #include "SoftAttachmentModel.h" -#include using namespace std; @@ -71,7 +59,8 @@ namespace render { auto avatarPtr = static_pointer_cast(avatar); if (avatarPtr->isInitialized() && args) { PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload"); - avatarPtr->render(args, qApp->getCamera()->getPosition()); + // TODO AVATARS_RENDERER: remove need for qApp + avatarPtr->render(args); } } template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) { @@ -85,7 +74,7 @@ namespace render { } } -Avatar::Avatar(RigPointer rig) : +Avatar::Avatar(QThread* thread, RigPointer rig) : AvatarData(), _skeletonOffset(0.0f), _bodyYawDelta(0.0f), @@ -108,7 +97,7 @@ Avatar::Avatar(RigPointer rig) : _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread - moveToThread(qApp->thread()); + moveToThread(thread); setScale(glm::vec3(1.0f)); // avatar scale is uniform @@ -128,7 +117,7 @@ Avatar::Avatar(RigPointer rig) : Avatar::~Avatar() { assert(isDead()); // mark dead before calling the dtor - auto treeRenderer = qApp->getEntities(); + auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (entityTree) { entityTree->withWriteLock([&] { @@ -237,7 +226,7 @@ void Avatar::updateAvatarEntities() { return; // wait until MyAvatar gets an ID before doing this. } - auto treeRenderer = qApp->getEntities(); + auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (!entityTree) { return; @@ -518,7 +507,7 @@ static TextRenderer3D* textRenderer(TextRendererType type) { return displayNameRenderer; } -void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr scene, render::Transaction& transaction) { +void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); _renderItemID = scene->allocateID(); @@ -530,7 +519,7 @@ void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr } } -void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, render::Transaction& transaction) { +void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_renderItemID); render::Item::clearID(_renderItemID); _skeletonModel->removeFromScene(scene, transaction); @@ -578,11 +567,13 @@ void Avatar::postUpdate(float deltaTime) { } } -void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { - auto& batch = *renderArgs->_batch; +void Avatar::render(RenderArgs* renderArgs) { + auto& batch = *(renderArgs->_batch); PROFILE_RANGE_BATCH(batch, __FUNCTION__); - if (glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) { + glm::vec3 viewPos = renderArgs->getViewFrustum().getPosition(); + const float MAX_DISTANCE_SQUARED_FOR_SHOWING_POINTING_LASERS = 100.0f; // 10^2 + if (glm::distance2(viewPos, getPosition()) < MAX_DISTANCE_SQUARED_FOR_SHOWING_POINTING_LASERS) { auto geometryCache = DependencyManager::get(); // render pointing lasers @@ -641,23 +632,16 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } } - { // simple frustum check - ViewFrustum frustum; - if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) { - qApp->copyShadowViewFrustum(frustum); - } else { - qApp->copyDisplayViewFrustum(frustum); - } - if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) { - return; - } + ViewFrustum frustum = renderArgs->getViewFrustum(); + if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) { + return; } - glm::vec3 toTarget = cameraPosition - getPosition(); + glm::vec3 toTarget = frustum.getPosition() - getPosition(); float distanceToTarget = glm::length(toTarget); { - fixupModelsInScene(); + fixupModelsInScene(renderArgs->_scene); if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) { // add local lights @@ -686,8 +670,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float DISPLAYNAME_DISTANCE = 20.0f; setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); - auto cameraMode = qApp->getCamera()->getMode(); - if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) { + if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { auto& frustum = renderArgs->getViewFrustum(); auto textPosition = getDisplayNamePosition(); if (frustum.pointIntersectsFrustum(textPosition)) { @@ -712,12 +695,11 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { return glm::angleAxis(angle * proportion, axis); } -void Avatar::fixupModelsInScene() { +void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _attachmentsToDelete.clear(); // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene - render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) { _skeletonModel->removeFromScene(scene, transaction); @@ -1490,8 +1472,7 @@ QList Avatar::getSkeleton() { return QList(); } -void Avatar::addToScene(AvatarSharedPointer myHandle) { - render::ScenePointer scene = qApp->getMain3DScene(); +void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) { if (scene) { render::Transaction transaction; auto nodelist = DependencyManager::get(); @@ -1505,8 +1486,9 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) { qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown"; } } -void Avatar::ensureInScene(AvatarSharedPointer self) { + +void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene) { if (!render::Item::isValidID(_renderItemID)) { - addToScene(self); + addToScene(self, scene); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 7b9d331048..8c055885fd 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -14,17 +14,15 @@ #include #include -#include #include #include #include - #include + #include "Head.h" #include "SkeletonModel.h" -#include "world.h" #include "Rig.h" #include @@ -68,7 +66,7 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: - explicit Avatar(RigPointer rig = nullptr); + explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); typedef render::Payload Payload; @@ -79,12 +77,12 @@ public: void simulate(float deltaTime, bool inView); virtual void simulateAttachments(float deltaTime); - virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition); + virtual void render(RenderArgs* renderArgs); - void addToScene(AvatarSharedPointer self, std::shared_ptr scene, + void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction); - void removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, + void removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction); void updateRenderItem(render::Transaction& transaction); @@ -305,7 +303,7 @@ protected: Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const; void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const; virtual bool shouldRenderHead(const RenderArgs* renderArgs) const; - virtual void fixupModelsInScene(); + virtual void fixupModelsInScene(const render::ScenePointer& scene); virtual void updatePalms(); @@ -316,8 +314,8 @@ protected: ThreadSafeValueCache _rightPalmPositionCache { glm::vec3() }; ThreadSafeValueCache _rightPalmRotationCache { glm::quat() }; - void addToScene(AvatarSharedPointer self); - void ensureInScene(AvatarSharedPointer self); + void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); + void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); bool isInScene() const { return render::Item::isValidID(_renderItemID); } // Some rate tracking support diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index f1170a7085..585776b395 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -69,7 +69,7 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) { AvatarManager::AvatarManager(QObject* parent) : _avatarsToFade(), - _myAvatar(std::make_shared(std::make_shared())) + _myAvatar(std::make_shared(qApp->thread(), std::make_shared())) { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); @@ -105,7 +105,7 @@ void AvatarManager::init() { this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection); if (_shouldRender) { - render::ScenePointer scene = qApp->getMain3DScene(); + const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; _myAvatar->addToScene(_myAvatar, scene, transaction); scene->enqueueTransaction(transaction); @@ -198,7 +198,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // for ALL avatars... if (_shouldRender) { - avatar->ensureInScene(avatar); + avatar->ensureInScene(avatar, qApp->getMain3DScene()); } if (!avatar->getMotionState()) { ShapeInfo shapeInfo; @@ -306,12 +306,10 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { if (avatar->getTargetScale() <= MIN_FADE_SCALE) { // fading to zero is such a rare event we push unique transaction for each one if (avatar->isInScene()) { - render::ScenePointer scene = qApp->getMain3DScene(); + const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; avatar->removeFromScene(*itr, scene, transaction); - if (scene) { - scene->enqueueTransaction(transaction); - } + scene->enqueueTransaction(transaction); } // only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine @@ -329,7 +327,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return std::make_shared(std::make_shared()); + return std::make_shared(qApp->thread(), std::make_shared()); } void AvatarManager::processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode) { @@ -343,19 +341,19 @@ void AvatarManager::processAvatarDataPacket(QSharedPointer mess if (avatar->isInScene()) { if (!_shouldRender) { // rare transition so we process the transaction immediately - render::ScenePointer scene = qApp->getMain3DScene(); + const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; + avatar->removeFromScene(avatar, scene, transaction); if (scene) { - render::Transaction transaction; - avatar->removeFromScene(avatar, scene, transaction); scene->enqueueTransaction(transaction); } } } else if (_shouldRender) { // very rare transition so we process the transaction immediately - render::ScenePointer scene = qApp->getMain3DScene(); + const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; + avatar->addToScene(avatar, scene, transaction); if (scene) { - render::Transaction transaction; - avatar->addToScene(avatar, scene, transaction); scene->enqueueTransaction(transaction); } } @@ -394,7 +392,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar void AvatarManager::clearOtherAvatars() { // Remove other avatars from the world but don't actually remove them from _avatarHash // each will either be removed on timeout or will re-added to the world on receipt of update. - render::ScenePointer scene = qApp->getMain3DScene(); + const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; QReadLocker locker(&_hashLock); @@ -414,9 +412,7 @@ void AvatarManager::clearOtherAvatars() { } ++avatarIterator; } - if (scene) { - scene->enqueueTransaction(transaction); - } + scene->enqueueTransaction(transaction); _myAvatar->clearLookAtTargetAvatar(); } @@ -522,7 +518,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { _shouldRender = shouldRenderAvatars; - render::ScenePointer scene = qApp->getMain3DScene(); + const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; if (_shouldRender) { for (auto avatarData : _avatarHash) { @@ -535,9 +531,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { avatar->removeFromScene(avatar, scene, transaction); } } - if (scene) { - scene->enqueueTransaction(transaction); - } + scene->enqueueTransaction(transaction); } AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index b86de68894..6eabbd081f 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -23,6 +23,7 @@ #include #include "Avatar.h" +#include "MyAvatar.h" #include "AvatarMotionState.h" #include "ScriptAvatar.h" @@ -42,6 +43,7 @@ public: void init(); std::shared_ptr getMyAvatar() { return _myAvatar; } + glm::vec3 getMyAvatarPosition() const { return _myAvatar->getPosition(); } // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); } @@ -110,7 +112,7 @@ private: QVector _avatarsToFade; - SetOfAvatarMotionStates _motionStatesThatMightUpdate; + QSet _motionStatesThatMightUpdate; VectorOfMotionStates _motionStatesToRemoveFromPhysics; SetOfMotionStates _motionStatesToAddToPhysics; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 66824d6e37..98b2b69373 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -83,6 +83,4 @@ protected: uint32_t _dirtyFlags; }; -typedef QSet SetOfAvatarMotionStates; - #endif // hifi_AvatarMotionState_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index da09bfba7e..f4f078c9e5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -48,7 +48,6 @@ #include "AvatarActionHold.h" #include "Menu.h" #include "MyAvatar.h" -#include "Physics.h" #include "Util.h" #include "InterfaceLogging.h" #include "DebugDraw.h" @@ -83,8 +82,8 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -MyAvatar::MyAvatar(RigPointer rig) : - Avatar(rig), +MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : + Avatar(thread, rig), _wasPushing(false), _isPushing(false), _isBeingPushed(false), @@ -260,7 +259,7 @@ void MyAvatar::simulateAttachments(float deltaTime) { } QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) { - CameraMode mode = qApp->getCamera()->getMode(); + CameraMode mode = qApp->getCamera().getMode(); _globalPosition = getPosition(); _globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius(); _globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight(); @@ -752,13 +751,12 @@ controller::Pose MyAvatar::getRightHandTipPose() const { } // virtual -void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { +void MyAvatar::render(RenderArgs* renderArgs) { // don't render if we've been asked to disable local rendering if (!_shouldRender) { return; // exit early } - - Avatar::render(renderArgs, cameraPosition); + Avatar::render(renderArgs); } void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { @@ -927,8 +925,7 @@ void MyAvatar::setEnableDebugDrawIKTargets(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - render::ScenePointer scene = qApp->getMain3DScene(); - _skeletonModel->setVisibleInScene(isEnabled, scene); + _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene()); } void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) { @@ -1079,7 +1076,7 @@ void MyAvatar::updateLookAtTargetAvatar() { _targetAvatarPosition = glm::vec3(0.0f); glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; - glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; @@ -1225,8 +1222,7 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); - render::ScenePointer scene = qApp->getMain3DScene(); - _skeletonModel->setVisibleInScene(true, scene); + _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); _headBoneSet.clear(); } @@ -1275,7 +1271,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) } glm::vec3 MyAvatar::getSkeletonPosition() const { - CameraMode mode = qApp->getCamera()->getMode(); + CameraMode mode = qApp->getCamera().getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. @@ -1540,7 +1536,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved); } -void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) { +void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) { if (model->isActive() && model->isRenderable()) { model->setVisibleInScene(visible, scene); } @@ -1634,8 +1630,7 @@ void MyAvatar::postUpdate(float deltaTime) { Avatar::postUpdate(deltaTime); - render::ScenePointer scene = qApp->getMain3DScene(); - if (DependencyManager::get()->shouldRenderAvatars() && _skeletonModel->initWhenReady(scene)) { + if (DependencyManager::get()->shouldRenderAvatars() && _skeletonModel->initWhenReady(qApp->getMain3DScene())) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); @@ -1704,13 +1699,13 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f; bool MyAvatar::cameraInsideHead() const { - const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); + const glm::vec3 cameraPosition = qApp->getCamera().getPosition(); return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; - bool firstPerson = qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON; + bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; bool insideHead = cameraInsideHead(); return !defaultMode || !firstPerson || !insideHead; } @@ -2265,7 +2260,7 @@ glm::vec3 MyAvatar::getPositionForAudio() { case AudioListenerMode::FROM_HEAD: return getHead()->getPosition(); case AudioListenerMode::FROM_CAMERA: - return qApp->getCamera()->getPosition(); + return qApp->getCamera().getPosition(); case AudioListenerMode::CUSTOM: return _customListenPosition; } @@ -2277,7 +2272,7 @@ glm::quat MyAvatar::getOrientationForAudio() { case AudioListenerMode::FROM_HEAD: return getHead()->getFinalOrientationInWorldFrame(); case AudioListenerMode::FROM_CAMERA: - return qApp->getCamera()->getOrientation(); + return qApp->getCamera().getOrientation(); case AudioListenerMode::CUSTOM: return _customListenOrientation; } @@ -2367,7 +2362,7 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) { } bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { - auto cameraMode = qApp->getCamera()->getMode(); + auto cameraMode = qApp->getCamera().getMode(); if (cameraMode == CAMERA_MODE_THIRD_PERSON) { return false; } else { @@ -2515,8 +2510,8 @@ void MyAvatar::setAway(bool value) { glm::mat4 MyAvatar::computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const { // Fetch the current camera transform. - glm::mat4 cameraWorldMatrix = qApp->getCamera()->getTransform(); - if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) { + glm::mat4 cameraWorldMatrix = qApp->getCamera().getTransform(); + if (qApp->getCamera().getMode() == CAMERA_MODE_MIRROR) { cameraWorldMatrix *= createMatFromScaleQuatAndPos(vec3(-1.0f, 1.0f, 1.0f), glm::quat(), glm::vec3()); } @@ -2561,7 +2556,7 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const { Transform avatarTransform; Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); - return glmExtractRotation(invAvatarMat * qApp->getCamera()->getTransform()); + return glmExtractRotation(invAvatarMat * qApp->getCamera().getTransform()); } default: { return Avatar::getAbsoluteJointRotationInObjectFrame(index); @@ -2598,7 +2593,7 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const { Transform avatarTransform; Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); - return extractTranslation(invAvatarMat * qApp->getCamera()->getTransform()); + return extractTranslation(invAvatarMat * qApp->getCamera().getTransform()); } default: { return Avatar::getAbsoluteJointTranslationInObjectFrame(index); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 097d3a1059..74af44c99a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -146,7 +146,7 @@ public: }; Q_ENUM(DriveKeys) - explicit MyAvatar(RigPointer rig); + explicit MyAvatar(QThread* thread, RigPointer rig); ~MyAvatar(); void registerMetaTypes(QScriptEngine* engine); @@ -525,7 +525,7 @@ private: void simulate(float deltaTime); void updateFromTrackers(float deltaTime); - virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; + virtual void render(RenderArgs* renderArgs) override; virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } bool getShouldRenderLocally() const { return _shouldRender; } @@ -551,7 +551,7 @@ private: // These are made private for MyAvatar so that you will use the "use" methods instead virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; - void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); + void setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visiblity); // derive avatar body position and orientation from the current HMD Sensor location. // results are in HMD frame diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index e2fed40a6d..95bf5eb028 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -105,9 +105,9 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* conte } bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { - Camera* camera = qApp->getCamera(); - glm::vec3 position = camera->getPosition(); - glm::quat orientation = camera->getOrientation(); + const Camera& camera = qApp->getCamera(); + glm::vec3 position = camera.getPosition(); + glm::quat orientation = camera.getOrientation(); glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); diff --git a/interface/src/ui/overlays/Billboardable.cpp b/interface/src/ui/overlays/Billboardable.cpp index a6226a46b0..a01d62bfd1 100644 --- a/interface/src/ui/overlays/Billboardable.cpp +++ b/interface/src/ui/overlays/Billboardable.cpp @@ -31,7 +31,7 @@ QVariant Billboardable::getProperty(const QString &property) { void Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offsetRotation) { if (isFacingAvatar()) { glm::vec3 billboardPos = transform.getTranslation(); - glm::vec3 cameraPos = qApp->getCamera()->getPosition(); + glm::vec3 cameraPos = qApp->getCamera().getPosition(); glm::vec3 look = cameraPos - billboardPos; float elevation = -asinf(look.y / glm::length(look)); float azimuth = atan2f(look.x, look.z); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 98ca7110ae..ccaa1d4fbc 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -58,13 +58,13 @@ void ModelOverlay::update(float deltatime) { _isLoaded = _model->isActive(); } -bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction) { +bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { Volume3DOverlay::addToScene(overlay, scene, transaction); _model->addToScene(scene, transaction); return true; } -void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction) { +void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { Volume3DOverlay::removeFromScene(overlay, scene, transaction); _model->removeFromScene(scene, transaction); } diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 245688156f..a3ddeed480 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -36,8 +36,8 @@ public: virtual ModelOverlay* createClone() const override; - virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction) override; - virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction) override; + virtual bool addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) override; void locationChanged(bool tellPhysics) override; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index e3004bd9c6..b650da3522 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -196,13 +196,13 @@ float Overlay::updatePulse() { return _pulse; } -bool Overlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction) { +bool Overlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { _renderItemID = scene->allocateID(); transaction.resetItem(_renderItemID, std::make_shared(overlay)); return true; } -void Overlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction) { +void Overlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_renderItemID); render::Item::clearID(_renderItemID); } diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 9849c71a1f..4ad1b070b1 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -48,8 +48,8 @@ public: virtual AABox getBounds() const = 0; virtual bool supportsGetProperty() const { return true; } - virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction); - virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::Transaction& transaction); + virtual bool addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction); + virtual void removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction); virtual const render::ShapeKey getShapeKey() { return render::ShapeKey::Builder::ownPipeline(); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 2f053924ab..1c08707e82 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -54,7 +54,7 @@ namespace render { // Mixin class for implementing basic single item rendering class SimpleRenderableEntityItem { public: - bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) { + bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { _myItem = scene->allocateID(); auto renderData = std::make_shared(self, _myItem); @@ -69,7 +69,7 @@ public: return true; } - void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) { + void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myItem); render::Item::clearID(_myItem); } @@ -99,8 +99,8 @@ private: #define SIMPLE_RENDERABLE() \ public: \ - virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \ - virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \ + virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \ + virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \ virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \ virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \ void checkFading() { \ diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 0b8592e678..cc292759f0 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -27,7 +27,7 @@ RenderableLightEntityItem::RenderableLightEntityItem(const EntityItemID& entityI { } -bool RenderableLightEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) { +bool RenderableLightEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { _myItem = scene->allocateID(); auto renderItem = std::make_shared(); @@ -51,7 +51,7 @@ void RenderableLightEntityItem::somethingChangedNotification() { LightEntityItem::somethingChangedNotification(); } -void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) { +void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myItem); render::Item::clearID(_myItem); } diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.h b/libraries/entities-renderer/src/RenderableLightEntityItem.h index 42a5dbc91a..3676023bed 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.h @@ -30,10 +30,10 @@ public: void updateLightFromEntity(render::Transaction& transaction); - virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; + virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void somethingChangedNotification() override; - virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; + virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void locationChanged(bool tellPhysics = true) override; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 99669ef3bf..8b1ba75259 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -91,13 +91,13 @@ bool RenderableModelEntityItem::setProperties(const EntityItemProperties& proper return somethingChanged; } -int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { QString oldModelURL = getModelURL(); - int bytesRead = ModelEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead, - args, propertyFlags, + int bytesRead = ModelEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead, + args, propertyFlags, overwriteLocalData, somethingChanged); if (oldModelURL != getModelURL()) { _needsModelReload = true; @@ -137,7 +137,7 @@ void RenderableModelEntityItem::remapTextures() { if (!_model) { return; // nothing to do if we don't have a model } - + if (!_model->isLoaded()) { return; // nothing to do if the model has not yet loaded } @@ -189,16 +189,16 @@ public: RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ } typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - + EntityItemPointer entity; }; namespace render { - template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) { + template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) { return ItemKey::Builder::opaqueShape().withTypeMeta(); } - - template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) { + + template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) { if (payload && payload->entity) { bool success; auto result = payload->entity->getAABox(success); @@ -228,7 +228,7 @@ namespace render { } } -bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, +bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { _myMetaItem = scene->allocateID(); @@ -249,7 +249,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p return true; } -void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, +void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myMetaItem); render::Item::clearID(_myMetaItem); @@ -437,7 +437,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->renderDebugMeshBoxes(batch); #endif - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene(); // 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 @@ -502,11 +502,11 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointerthread()) { return _model; } - + _needsModelReload = false; // this is the reload // If we have a URL, then we will want to end up returning a model... @@ -526,7 +526,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointergetMain3DScene(); + const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::Transaction transaction; _model->removeFromScene(scene, transaction); scene->enqueueTransaction(transaction); @@ -552,7 +552,7 @@ void RenderableModelEntityItem::update(const quint64& now) { properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it auto extents = _model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - qCDebug(entitiesrenderer) << "Autoresizing" << (!getName().isEmpty() ? getName() : getModelURL()) + qCDebug(entitiesrenderer) << "Autoresizing" << (!getName().isEmpty() ? getName() : getModelURL()) << "from mesh extents"; QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", Qt::QueuedConnection, @@ -594,8 +594,8 @@ bool RenderableModelEntityItem::supportsDetailedRayIntersection() const { return _model && _model->isLoaded(); } -bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, +bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const { if (!_model) { return true; @@ -1239,11 +1239,10 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics) { return; } - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::Transaction transaction; transaction.updateItem(myMetaItem); - scene->enqueueTransaction(transaction); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); }); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 7eab5172d8..b440aacd7e 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -40,8 +40,8 @@ public: void doInitialModelSimulation(); - virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; + virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; void updateModelBounds(); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index fdf9ba888f..34dc86d92a 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -161,7 +161,7 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent } bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, - render::ScenePointer scene, + const render::ScenePointer& scene, render::Transaction& transaction) { _scene = scene; _renderItemId = _scene->allocateID(); @@ -176,7 +176,7 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, } void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self, - render::ScenePointer scene, + const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_renderItemId); _scene = nullptr; @@ -323,4 +323,4 @@ void RenderableParticleEffectEntityItem::notifyBoundChanged() { }); _scene->enqueueTransaction(transaction); -} \ No newline at end of file +} diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 9b31c92b07..daa6ba0691 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -25,8 +25,8 @@ public: void updateRenderItem(); - virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::Transaction& transaction) override; + virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; protected: virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index ee3b068af9..fd5346093b 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -816,7 +816,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { } bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, - std::shared_ptr scene, + const render::ScenePointer& scene, render::Transaction& transaction) { _myItem = scene->allocateID(); @@ -834,7 +834,7 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, } void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, - std::shared_ptr scene, + const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myItem); render::Item::clearID(_myItem); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index c0bc8ba610..ff97f09ee1 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -106,10 +106,10 @@ public: virtual void setZTextureURL(const QString& zTextureURL) override; virtual bool addToScene(EntityItemPointer self, - std::shared_ptr scene, + const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void removeFromScene(EntityItemPointer self, - std::shared_ptr scene, + const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void setXNNeighborID(const EntityItemID& xNNeighborID) override; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c9a45465c2..66495a7054 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -217,7 +217,7 @@ namespace render { } } -bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, +bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { _myMetaItem = scene->allocateID(); @@ -232,7 +232,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt return true; } -void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, +void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myMetaItem); render::Item::clearID(_myMetaItem); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index d86a22509b..c81afdab08 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -38,8 +38,8 @@ public: virtual void render(RenderArgs* args) override; virtual bool contains(const glm::vec3& point) const override; - virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::Transaction& transaction) override; + virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; render::ItemID getRenderItemID() const { return _myMetaItem; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3e1724d1ad..ff5f12b2f7 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -151,9 +151,9 @@ public: bool& somethingChanged) { somethingChanged = false; return 0; } - virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, + virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { return false; } // by default entity items don't add to scene - virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, + virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { } // by default entity items don't add to scene virtual void render(RenderArgs* args) { } // by default entity items don't know how to render diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 51d9c2ad25..89213459fa 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -429,7 +429,7 @@ void ModelEntityItem::setJointTranslations(const QVector& translation for (int index = 0; index < translations.size(); index++) { if (_localJointTranslationsSet[index]) { _localJointTranslations[index] = translations[index]; - _localJointTranslationsSet[index] = true; + _localJointTranslationsDirty[index] = true; } } }); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 0800c27839..d0db376a9d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -744,6 +744,10 @@ void GLBackend::recycle() const { glDeleteQueries((GLsizei)ids.size(), ids.data()); } } + + GLVariableAllocationSupport::manageMemory(); + GLVariableAllocationSupport::_frameTexturesCreated = 0; + } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 65c5788227..19891d3370 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -8,6 +8,9 @@ #include "GLTexture.h" +#include +#include + #include "GLBackend.h" using namespace gpu; @@ -111,6 +114,20 @@ GLTexture::~GLTexture() { } } +void GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { + return; + } + auto size = _gpuObject.evalMipDimensions(sourceMip); + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + if (mipData) { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + } else { + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + } +} + GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : Parent(backend, texture, id) { } @@ -127,3 +144,420 @@ GLExternalTexture::~GLExternalTexture() { const_cast(_id) = 0; } } + + +// Variable sized textures +using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; +using WorkQueue = GLVariableAllocationSupport::WorkQueue; + +std::list GLVariableAllocationSupport::_memoryManagedTextures; +MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; +std::atomic GLVariableAllocationSupport::_memoryPressureStateStale { false }; +const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; +WorkQueue GLVariableAllocationSupport::_transferQueue; +WorkQueue GLVariableAllocationSupport::_promoteQueue; +WorkQueue GLVariableAllocationSupport::_demoteQueue; +TexturePointer GLVariableAllocationSupport::_currentTransferTexture; +size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; + +#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f +#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) + +static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); + +using TransferJob = GLVariableAllocationSupport::TransferJob; + +const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; +const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4; + +#if THREADED_TEXTURE_BUFFERING +std::shared_ptr TransferJob::_bufferThread { nullptr }; +std::atomic TransferJob::_shutdownBufferingThread { false }; +Mutex TransferJob::_mutex; +TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; + +void TransferJob::startTransferLoop() { + if (_bufferThread) { + return; + } + _shutdownBufferingThread = false; + _bufferThread = std::make_shared([] { + TransferJob::bufferLoop(); + }); +} + +void TransferJob::stopTransferLoop() { + if (!_bufferThread) { + return; + } + _shutdownBufferingThread = true; + _bufferThread->join(); + _bufferThread.reset(); + _shutdownBufferingThread = false; +} +#endif + +TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) + : _parent(parent) { + + auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + GLenum format; + GLenum type; + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); + format = texelFormat.format; + type = texelFormat.type; + auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); + + + if (0 == lines) { + _transferSize = mipSize; + _bufferingLambda = [=] { + auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData(), _transferSize); + _bufferingCompleted = true; + }; + + } else { + transferDimensions.y = lines; + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + auto bytesPerLine = (uint32_t)mipSize / dimensions.y; + auto sourceOffset = bytesPerLine * lineOffset; + _transferSize = bytesPerLine * lines; + _bufferingLambda = [=] { + auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); + _bufferingCompleted = true; + }; + } + + Backend::updateTextureTransferPendingSize(0, _transferSize); + + _transferLambda = [=] { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); + std::vector emptyVector; + _buffer.swap(emptyVector); + }; +} + +TransferJob::TransferJob(const GLTexture& parent, std::function transferLambda) + : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { +} + +TransferJob::~TransferJob() { + Backend::updateTextureTransferPendingSize(_transferSize, 0); +} + + +bool TransferJob::tryTransfer() { + // Disable threaded texture transfer for now +#if THREADED_TEXTURE_BUFFERING + // Are we ready to transfer + if (_bufferingCompleted) { + _transferLambda(); + return true; + } + + startBuffering(); + return false; +#else + if (!_bufferingCompleted) { + _bufferingLambda(); + _bufferingCompleted = true; + } + _transferLambda(); + return true; +#endif +} + +#if THREADED_TEXTURE_BUFFERING + +void TransferJob::startBuffering() { + if (_bufferingStarted) { + return; + } + _bufferingStarted = true; + { + Lock lock(_mutex); + _bufferLambdaQueue.push(_bufferingLambda); + } +} + +void TransferJob::bufferLoop() { + while (!_shutdownBufferingThread) { + VoidLambdaQueue workingQueue; + { + Lock lock(_mutex); + _bufferLambdaQueue.swap(workingQueue); + } + + if (workingQueue.empty()) { + QThread::msleep(5); + continue; + } + + while (!workingQueue.empty()) { + workingQueue.front()(); + workingQueue.pop(); + } + } +} +#endif + +GLVariableAllocationSupport::GLVariableAllocationSupport() { + _memoryPressureStateStale = true; +} + +GLVariableAllocationSupport::~GLVariableAllocationSupport() { + _memoryPressureStateStale = true; +} + +void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) { + _memoryManagedTextures.push_back(texturePointer); + addToWorkQueue(texturePointer); +} + +void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) { + GLTexture* gltexture = Backend::getGPUObject(*texturePointer); + GLVariableAllocationSupport* vargltexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (vargltexture->canDemote()) { + // Demote largest first + _demoteQueue.push({ texturePointer, (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Undersubscribed: + if (vargltexture->canPromote()) { + // Promote smallest first + _promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Transfer: + if (vargltexture->hasPendingTransfers()) { + // Transfer priority given to smaller mips first + _transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) }); + } + break; + + case MemoryPressureState::Idle: + break; + + default: + Q_UNREACHABLE(); + } +} + +WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() { + static WorkQueue empty; + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return _demoteQueue; + + case MemoryPressureState::Undersubscribed: + return _promoteQueue; + + case MemoryPressureState::Transfer: + return _transferQueue; + + default: + break; + } + Q_UNREACHABLE(); + return empty; +} + +// FIXME hack for stats display +QString getTextureMemoryPressureModeString() { + switch (GLVariableAllocationSupport::_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return "Oversubscribed"; + + case MemoryPressureState::Undersubscribed: + return "Undersubscribed"; + + case MemoryPressureState::Transfer: + return "Transfer"; + + case MemoryPressureState::Idle: + return "Idle"; + } + Q_UNREACHABLE(); + return "Unknown"; +} + +void GLVariableAllocationSupport::updateMemoryPressure() { + static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale + // so that we react + if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { + _memoryPressureStateStale = true; + lastAllowedMemoryAllocation = allowedMemoryAllocation; + } + + if (!_memoryPressureStateStale.exchange(false)) { + return; + } + + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + + // Clear any defunct textures (weak pointers that no longer have a valid texture) + _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { + return weakPointer.expired(); + }); + + // Convert weak pointers to strong. This new list may still contain nulls if a texture was + // deleted on another thread between the previous line and this one + std::vector strongTextures; { + strongTextures.reserve(_memoryManagedTextures.size()); + std::transform( + _memoryManagedTextures.begin(), _memoryManagedTextures.end(), + std::back_inserter(strongTextures), + [](const TextureWeakPointer& p) { return p.lock(); }); + } + + size_t totalVariableMemoryAllocation = 0; + size_t idealMemoryAllocation = 0; + bool canDemote = false; + bool canPromote = false; + bool hasTransfers = false; + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + // Track how much the texture thinks it should be using + idealMemoryAllocation += texture->evalTotalSize(); + // Track how much we're actually using + totalVariableMemoryAllocation += gltexture->size(); + canDemote |= vartexture->canDemote(); + canPromote |= vartexture->canPromote(); + hasTransfers |= vartexture->hasPendingTransfers(); + } + + size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; + float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; + + auto newState = MemoryPressureState::Idle; + if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + newState = MemoryPressureState::Oversubscribed; + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + newState = MemoryPressureState::Undersubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } + + if (newState != _memoryPressureState) { +#if THREADED_TEXTURE_BUFFERING + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::stopTransferLoop(); + } + _memoryPressureState = newState; + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::startTransferLoop(); + } +#else + _memoryPressureState = newState; +#endif + // Clear the existing queue + _transferQueue = WorkQueue(); + _promoteQueue = WorkQueue(); + _demoteQueue = WorkQueue(); + + // Populate the existing textures into the queue + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + addToWorkQueue(texture); + } + } +} + +void GLVariableAllocationSupport::processWorkQueues() { + if (MemoryPressureState::Idle == _memoryPressureState) { + return; + } + + auto& workQueue = getActiveWorkQueue(); + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + while (!workQueue.empty()) { + auto workTarget = workQueue.top(); + workQueue.pop(); + auto texture = workTarget.first.lock(); + if (!texture) { + continue; + } + + // Grab the first item off the demote queue + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + if (MemoryPressureState::Oversubscribed == _memoryPressureState) { + if (!vartexture->canDemote()) { + continue; + } + vartexture->demote(); + _memoryPressureStateStale = true; + } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { + if (!vartexture->canPromote()) { + continue; + } + vartexture->promote(); + _memoryPressureStateStale = true; + } else if (MemoryPressureState::Transfer == _memoryPressureState) { + if (!vartexture->hasPendingTransfers()) { + continue; + } + vartexture->executeNextTransfer(texture); + } else { + Q_UNREACHABLE(); + } + + // Reinject into the queue if more work to be done + addToWorkQueue(texture); + break; + } + + if (workQueue.empty()) { + _memoryPressureStateStale = true; + } +} + +void GLVariableAllocationSupport::manageMemory() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + updateMemoryPressure(); + processWorkQueues(); +} + + +void GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) { + if (_populatedMip <= _allocatedMip) { + return; + } + + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + + if (!_pendingTransfers.empty()) { + // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture + _currentTransferTexture = currentTexture; + if (_pendingTransfers.front()->tryTransfer()) { + _pendingTransfers.pop(); + _currentTransferTexture.reset(); + } + } +} diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index b47aa3e8dd..960575827f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -11,6 +11,9 @@ #include "GLShared.h" #include "GLBackend.h" #include "GLTexelFormat.h" +#include + +#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl { @@ -19,9 +22,124 @@ struct GLFilterMode { GLint magFilter; }; +class GLVariableAllocationSupport { + friend class GLBackend; + +public: + GLVariableAllocationSupport(); + virtual ~GLVariableAllocationSupport(); + + enum class MemoryPressureState { + Idle, + Transfer, + Oversubscribed, + Undersubscribed, + }; + + using QueuePair = std::pair; + struct QueuePairLess { + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } + }; + using WorkQueue = std::priority_queue, QueuePairLess>; + + class TransferJob { + using VoidLambda = std::function; + using VoidLambdaQueue = std::queue; + using ThreadPointer = std::shared_ptr; + const GLTexture& _parent; + // Holds the contents to transfer to the GPU in CPU memory + std::vector _buffer; + // Indicates if a transfer from backing storage to interal storage has started + bool _bufferingStarted { false }; + bool _bufferingCompleted { false }; + VoidLambda _transferLambda; + VoidLambda _bufferingLambda; + +#if THREADED_TEXTURE_BUFFERING + static Mutex _mutex; + static VoidLambdaQueue _bufferLambdaQueue; + static ThreadPointer _bufferThread; + static std::atomic _shutdownBufferingThread; + static void bufferLoop(); +#endif + + public: + TransferJob(const TransferJob& other) = delete; + TransferJob(const GLTexture& parent, std::function transferLambda); + TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + ~TransferJob(); + bool tryTransfer(); + +#if THREADED_TEXTURE_BUFFERING + static void startTransferLoop(); + static void stopTransferLoop(); +#endif + + private: + size_t _transferSize { 0 }; +#if THREADED_TEXTURE_BUFFERING + void startBuffering(); +#endif + void transfer(); + }; + + using TransferQueue = std::queue>; + static MemoryPressureState _memoryPressureState; + +public: + static void addMemoryManagedTexture(const TexturePointer& texturePointer); + +protected: + static size_t _frameTexturesCreated; + static std::atomic _memoryPressureStateStale; + static std::list _memoryManagedTextures; + static WorkQueue _transferQueue; + static WorkQueue _promoteQueue; + static WorkQueue _demoteQueue; + static TexturePointer _currentTransferTexture; + static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; + static const uvec3 MAX_TRANSFER_DIMENSIONS; + static const size_t MAX_TRANSFER_SIZE; + + + static void updateMemoryPressure(); + static void processWorkQueues(); + static void addToWorkQueue(const TexturePointer& texture); + static WorkQueue& getActiveWorkQueue(); + + static void manageMemory(); + + //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } + bool canPromote() const { return _allocatedMip > 0; } + bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } + void executeNextTransfer(const TexturePointer& currentTexture); + virtual void populateTransferQueue() = 0; + virtual void promote() = 0; + virtual void demote() = 0; + + // The allocated mip level, relative to the number of mips in the gpu::Texture object + // The relationship between a given glMip to the original gpu::Texture mip is always + // glMip + _allocatedMip + uint16 _allocatedMip { 0 }; + // The populated mip level, relative to the number of mips in the gpu::Texture object + // This must always be >= the allocated mip + uint16 _populatedMip { 0 }; + // The highest (lowest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _maxAllocatedMip { 0 }; + // Contains a series of lambdas that when executed will transfer data to the GPU, modify + // the _populatedMip and update the sampler in order to fully populate the allocated texture + // until _populatedMip == _allocatedMip + TransferQueue _pendingTransfers; +}; + class GLTexture : public GLObject { using Parent = GLObject; friend class GLBackend; + friend class GLVariableAllocationSupport; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; @@ -45,6 +163,8 @@ public: protected: virtual Size size() const = 0; virtual void generateMips() const = 0; + virtual void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const = 0; + virtual void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final; GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); }; @@ -57,6 +177,8 @@ public: protected: GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); void generateMips() const override {} + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override {} + Size size() const override { return 0; } }; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 93d65b74dd..0c3c70dbb8 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -45,8 +45,7 @@ public: protected: GL41Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; - void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; - void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override; virtual void syncSampler() const; void withPreservedTexture(std::function f) const; @@ -86,8 +85,29 @@ public: GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); }; - class GL41ResourceTexture : public GL41FixedAllocationTexture { - using Parent = GL41FixedAllocationTexture; + class GL41VariableAllocationTexture : public GL41Texture, public GLVariableAllocationSupport { + using Parent = GL41Texture; + friend class GL41Backend; + using PromoteLambda = std::function; + + + protected: + GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL41VariableAllocationTexture(); + + void allocateStorage(uint16 allocatedMip); + void syncSampler() const override; + void promote() override; + void demote() override; + void populateTransferQueue() override; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override; + + Size size() const override { return _size; } + Size _size { 0 }; + }; + + class GL41ResourceTexture : public GL41VariableAllocationTexture { + using Parent = GL41VariableAllocationTexture; friend class GL41Backend; protected: GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 2056085091..1e02b3a385 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -46,11 +46,11 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { object = new GL41StrictResourceTexture(shared_from_this(), texture); break; - case TextureUsageType::RESOURCE: { + case TextureUsageType::RESOURCE: qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str(); object = new GL41ResourceTexture(shared_from_this(), texture); + GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); break; - } default: Q_UNREACHABLE(); @@ -69,7 +69,9 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& GLuint GL41Texture::allocate(const Texture& texture) { GLuint result; - glGenTextures(1, &result); + // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension + glCreateTextures(getGLTextureType(texture), 1, &result); + //glGenTextures(1, &result); return result; } @@ -105,20 +107,6 @@ void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const (void)CHECK_GL_ERROR(); } -void GL41Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { - return; - } - auto size = _gpuObject.evalMipDimensions(sourceMip); - auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - if (mipData) { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); - } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); - } -} - void GL41Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); @@ -216,29 +204,217 @@ GL41StrictResourceTexture::GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41Texture(backend, texture) { + auto mipLevels = texture.getNumMips(); + _allocatedMip = mipLevels; + uvec3 mipDimensions; + for (uint16_t mip = 0; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + _maxAllocatedMip = _populatedMip = mip; + break; + } + } + + uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); + allocateStorage(allocatedMip); + _memoryPressureStateStale = true; + size_t maxFace = GLTexture::getFaceCount(_target); + for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip - _allocatedMip; + for (uint8_t face = 0; face < maxFace; ++face) { + copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + syncSampler(); +} + +GL41VariableAllocationTexture::~GL41VariableAllocationTexture() { + Backend::updateTextureGPUMemoryUsage(_size, 0); +} + +void GL41VariableAllocationTexture::allocateStorage(uint16 allocatedMip) { + _allocatedMip = allocatedMip; + + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); + const auto totalMips = _gpuObject.getNumMips(); + const auto mips = totalMips - _allocatedMip; + withPreservedTexture([&] { + // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension + glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + }); + auto mipLevels = _gpuObject.getNumMips(); + _size = 0; + for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { + _size += _gpuObject.evalMipSize(mip); + } + Backend::updateTextureGPUMemoryUsage(0, _size); +} + + +void GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const { + withPreservedTexture([&] { + Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, format, type, sourcePointer); + }); +} + +void GL41VariableAllocationTexture::syncSampler() const { + withPreservedTexture([&] { + Parent::syncSampler(); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + }); +} + +void GL41VariableAllocationTexture::promote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip > 0); + GLuint oldId = _id; + auto oldSize = _size; + // create new texture + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + + // allocate storage for new level + allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); + + withPreservedTexture([&] { + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + syncSampler(); + }); + + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + populateTransferQueue(); +} + +void GL41VariableAllocationTexture::demote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip < _maxAllocatedMip); + auto oldId = _id; + auto oldSize = _size; + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + allocateStorage(_allocatedMip + 1); + _populatedMip = std::max(_populatedMip, _allocatedMip); + + withPreservedTexture([&] { + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + syncSampler(); + }); + + + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + populateTransferQueue(); +} + + +void GL41VariableAllocationTexture::populateTransferQueue() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + if (_populatedMip <= _allocatedMip) { + return; + } + _pendingTransfers = TransferQueue(); + + const uint8_t maxFace = GLTexture::getFaceCount(_target); + uint16_t sourceMip = _populatedMip; + do { + --sourceMip; + auto targetMip = sourceMip - _allocatedMip; + auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + for (uint8_t face = 0; face < maxFace; ++face) { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + continue; + } + + // If the mip is less than the max transfer size, then just do it in one transfer + if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + // Can the mip be transferred in one go + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + continue; + } + + // break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); + const auto lines = mipDimensions.y; + auto bytesPerLine = mipSize / lines; + Q_ASSERT(0 == (mipSize % lines)); + uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + uint32_t lineOffset = 0; + while (lineOffset < lines) { + uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); + lineOffset += linesToCopy; + } + } + + // queue up the sampler and populated mip change for after the transfer has completed + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } while (sourceMip != _allocatedMip); +} + // resource textures using GL41ResourceTexture = GL41Backend::GL41ResourceTexture; -GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41FixedAllocationTexture(backend, texture) { - Backend::updateTextureGPUMemoryUsage(0, size()); - - withPreservedTexture([&] { - - auto mipLevels = _gpuObject.getNumMips(); - for (uint16_t sourceMip = 0; sourceMip < mipLevels; sourceMip++) { - uint16_t targetMip = sourceMip; - size_t maxFace = GLTexture::getFaceCount(_target); - for (uint8_t face = 0; face < maxFace; face++) { - copyMipFaceFromTexture(sourceMip, targetMip, face); - } - } - }); - +GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41VariableAllocationTexture(backend, texture) { if (texture.isAutogenerateMips()) { generateMips(); } } GL41ResourceTexture::~GL41ResourceTexture() { - Backend::updateTextureGPUMemoryUsage(size(), 0); } + + diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index 12c4b818f7..c9d1fb1b2f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -20,8 +20,6 @@ using namespace gpu::gl45; void GL45Backend::recycle() const { Parent::recycle(); - GL45VariableAllocationTexture::manageMemory(); - GL45VariableAllocationTexture::_frameTexturesCreated = 0; } void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index ca8028aff6..152d4cfec5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -40,8 +40,7 @@ public: protected: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; - void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; - void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override; virtual void syncSampler() const; }; @@ -83,116 +82,17 @@ public: // Textures that can be managed at runtime to increase or decrease their memory load // - class GL45VariableAllocationTexture : public GL45Texture { + class GL45VariableAllocationTexture : public GL45Texture, public GLVariableAllocationSupport { using Parent = GL45Texture; friend class GL45Backend; using PromoteLambda = std::function; - public: - enum class MemoryPressureState { - Idle, - Transfer, - Oversubscribed, - Undersubscribed, - }; - - using QueuePair = std::pair; - struct QueuePairLess { - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } - }; - using WorkQueue = std::priority_queue, QueuePairLess>; - - class TransferJob { - using VoidLambda = std::function; - using VoidLambdaQueue = std::queue; - using ThreadPointer = std::shared_ptr; - const GL45VariableAllocationTexture& _parent; - // Holds the contents to transfer to the GPU in CPU memory - std::vector _buffer; - // Indicates if a transfer from backing storage to interal storage has started - bool _bufferingStarted { false }; - bool _bufferingCompleted { false }; - VoidLambda _transferLambda; - VoidLambda _bufferingLambda; -#if THREADED_TEXTURE_BUFFERING - static Mutex _mutex; - static VoidLambdaQueue _bufferLambdaQueue; - static ThreadPointer _bufferThread; - static std::atomic _shutdownBufferingThread; - static void bufferLoop(); -#endif - - public: - TransferJob(const TransferJob& other) = delete; - TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); - TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); - ~TransferJob(); - bool tryTransfer(); - -#if THREADED_TEXTURE_BUFFERING - static void startTransferLoop(); - static void stopTransferLoop(); -#endif - - private: - size_t _transferSize { 0 }; -#if THREADED_TEXTURE_BUFFERING - void startBuffering(); -#endif - void transfer(); - }; - - using TransferQueue = std::queue>; - static MemoryPressureState _memoryPressureState; - protected: - static size_t _frameTexturesCreated; - static std::atomic _memoryPressureStateStale; - static std::list _memoryManagedTextures; - static WorkQueue _transferQueue; - static WorkQueue _promoteQueue; - static WorkQueue _demoteQueue; - static TexturePointer _currentTransferTexture; - static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - - - static void updateMemoryPressure(); - static void processWorkQueues(); - static void addMemoryManagedTexture(const TexturePointer& texturePointer); - static void addToWorkQueue(const TexturePointer& texture); - static WorkQueue& getActiveWorkQueue(); - - static void manageMemory(); protected: GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45VariableAllocationTexture(); - //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0; } - bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } - void executeNextTransfer(const TexturePointer& currentTexture); Size size() const override { return _size; } - virtual void populateTransferQueue() = 0; - virtual void promote() = 0; - virtual void demote() = 0; - - // The allocated mip level, relative to the number of mips in the gpu::Texture object - // The relationship between a given glMip to the original gpu::Texture mip is always - // glMip + _allocatedMip - uint16 _allocatedMip { 0 }; - // The populated mip level, relative to the number of mips in the gpu::Texture object - // This must always be >= the allocated mip - uint16 _populatedMip { 0 }; - // The highest (lowest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _maxAllocatedMip { 0 }; Size _size { 0 }; - // Contains a series of lambdas that when executed will transfer data to the GPU, modify - // the _populatedMip and update the sampler in order to fully populate the allocated texture - // until _populatedMip == _allocatedMip - TransferQueue _pendingTransfers; }; class GL45ResourceTexture : public GL45VariableAllocationTexture { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index ab4153c04c..73974addff 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -67,7 +67,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { #else object = new GL45ResourceTexture(shared_from_this(), texture); #endif - GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); + GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); } else { auto fallback = texturePointer->getFallbackTexture(); if (fallback) { @@ -135,20 +135,6 @@ void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { - return; - } - auto size = _gpuObject.evalMipDimensions(sourceMip); - auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - if (mipData) { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); - } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); - } -} - void GL45Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index a614d62221..a453d4207d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -27,416 +27,16 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -// Variable sized textures using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; -using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; -using WorkQueue = GL45VariableAllocationTexture::WorkQueue; - -std::list GL45VariableAllocationTexture::_memoryManagedTextures; -MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; -std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; -const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GL45VariableAllocationTexture::_transferQueue; -WorkQueue GL45VariableAllocationTexture::_promoteQueue; -WorkQueue GL45VariableAllocationTexture::_demoteQueue; -TexturePointer GL45VariableAllocationTexture::_currentTransferTexture; - -#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f -#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f -#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) - -static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); - -using TransferJob = GL45VariableAllocationTexture::TransferJob; - -static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; -static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; - -#if THREADED_TEXTURE_BUFFERING -std::shared_ptr TransferJob::_bufferThread { nullptr }; -std::atomic TransferJob::_shutdownBufferingThread { false }; -Mutex TransferJob::_mutex; -TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; - -void TransferJob::startTransferLoop() { - if (_bufferThread) { - return; - } - _shutdownBufferingThread = false; - _bufferThread = std::make_shared([] { - TransferJob::bufferLoop(); - }); -} - -void TransferJob::stopTransferLoop() { - if (!_bufferThread) { - return; - } - _shutdownBufferingThread = true; - _bufferThread->join(); - _bufferThread.reset(); - _shutdownBufferingThread = false; -} -#endif - -TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent) { - - auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - GLenum format; - GLenum type; - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); - format = texelFormat.format; - type = texelFormat.type; - auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); - - - if (0 == lines) { - _transferSize = mipSize; - _bufferingLambda = [=] { - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData(), _transferSize); - _bufferingCompleted = true; - }; - - } else { - transferDimensions.y = lines; - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto sourceOffset = bytesPerLine * lineOffset; - _transferSize = bytesPerLine * lines; - _bufferingLambda = [=] { - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); - _bufferingCompleted = true; - }; - } - - Backend::updateTextureTransferPendingSize(0, _transferSize); - - _transferLambda = [=] { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); - std::vector emptyVector; - _buffer.swap(emptyVector); - }; -} - -TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) - : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { -} - -TransferJob::~TransferJob() { - Backend::updateTextureTransferPendingSize(_transferSize, 0); -} - - -bool TransferJob::tryTransfer() { - // Disable threaded texture transfer for now -#if THREADED_TEXTURE_BUFFERING - // Are we ready to transfer - if (_bufferingCompleted) { - _transferLambda(); - return true; - } - - startBuffering(); - return false; -#else - if (!_bufferingCompleted) { - _bufferingLambda(); - _bufferingCompleted = true; - } - _transferLambda(); - return true; -#endif -} - -#if THREADED_TEXTURE_BUFFERING - -void TransferJob::startBuffering() { - if (_bufferingStarted) { - return; - } - _bufferingStarted = true; - { - Lock lock(_mutex); - _bufferLambdaQueue.push(_bufferingLambda); - } -} - -void TransferJob::bufferLoop() { - while (!_shutdownBufferingThread) { - VoidLambdaQueue workingQueue; - { - Lock lock(_mutex); - _bufferLambdaQueue.swap(workingQueue); - } - - if (workingQueue.empty()) { - QThread::msleep(5); - continue; - } - - while (!workingQueue.empty()) { - workingQueue.front()(); - workingQueue.pop(); - } - } -} -#endif - - -void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { - _memoryManagedTextures.push_back(texturePointer); - addToWorkQueue(texturePointer); -} - -void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texturePointer) { - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (object->canDemote()) { - // Demote largest first - _demoteQueue.push({ texturePointer, (float)object->size() }); - } - break; - - case MemoryPressureState::Undersubscribed: - if (object->canPromote()) { - // Promote smallest first - _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); - } - break; - - case MemoryPressureState::Transfer: - if (object->hasPendingTransfers()) { - // Transfer priority given to smaller mips first - _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); - } - break; - - case MemoryPressureState::Idle: - break; - - default: - Q_UNREACHABLE(); - } -} - -WorkQueue& GL45VariableAllocationTexture::getActiveWorkQueue() { - static WorkQueue empty; - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return _demoteQueue; - - case MemoryPressureState::Undersubscribed: - return _promoteQueue; - - case MemoryPressureState::Transfer: - return _transferQueue; - - default: - break; - } - Q_UNREACHABLE(); - return empty; -} - -// FIXME hack for stats display -QString getTextureMemoryPressureModeString() { - switch (GL45VariableAllocationTexture::_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return "Oversubscribed"; - - case MemoryPressureState::Undersubscribed: - return "Undersubscribed"; - - case MemoryPressureState::Transfer: - return "Transfer"; - - case MemoryPressureState::Idle: - return "Idle"; - } - Q_UNREACHABLE(); - return "Unknown"; -} - -void GL45VariableAllocationTexture::updateMemoryPressure() { - static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - - size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale - // so that we react - if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { - _memoryPressureStateStale = true; - lastAllowedMemoryAllocation = allowedMemoryAllocation; - } - - if (!_memoryPressureStateStale.exchange(false)) { - return; - } - - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - // Clear any defunct textures (weak pointers that no longer have a valid texture) - _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { - return weakPointer.expired(); - }); - - // Convert weak pointers to strong. This new list may still contain nulls if a texture was - // deleted on another thread between the previous line and this one - std::vector strongTextures; { - strongTextures.reserve(_memoryManagedTextures.size()); - std::transform( - _memoryManagedTextures.begin(), _memoryManagedTextures.end(), - std::back_inserter(strongTextures), - [](const TextureWeakPointer& p) { return p.lock(); }); - } - - size_t totalVariableMemoryAllocation = 0; - size_t idealMemoryAllocation = 0; - bool canDemote = false; - bool canPromote = false; - bool hasTransfers = false; - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - // Track how much the texture thinks it should be using - idealMemoryAllocation += texture->evalTotalSize(); - // Track how much we're actually using - totalVariableMemoryAllocation += object->size(); - canDemote |= object->canDemote(); - canPromote |= object->canPromote(); - hasTransfers |= object->hasPendingTransfers(); - } - - size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; - - auto newState = MemoryPressureState::Idle; - if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { - newState = MemoryPressureState::Undersubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; - } - - if (newState != _memoryPressureState) { -#if THREADED_TEXTURE_BUFFERING - if (MemoryPressureState::Transfer == _memoryPressureState) { - TransferJob::stopTransferLoop(); - } - _memoryPressureState = newState; - if (MemoryPressureState::Transfer == _memoryPressureState) { - TransferJob::startTransferLoop(); - } -#else - _memoryPressureState = newState; -#endif - // Clear the existing queue - _transferQueue = WorkQueue(); - _promoteQueue = WorkQueue(); - _demoteQueue = WorkQueue(); - - // Populate the existing textures into the queue - for (const auto& texture : strongTextures) { - addToWorkQueue(texture); - } - } -} - -void GL45VariableAllocationTexture::processWorkQueues() { - if (MemoryPressureState::Idle == _memoryPressureState) { - return; - } - - auto& workQueue = getActiveWorkQueue(); - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - while (!workQueue.empty()) { - auto workTarget = workQueue.top(); - workQueue.pop(); - auto texture = workTarget.first.lock(); - if (!texture) { - continue; - } - - // Grab the first item off the demote queue - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (MemoryPressureState::Oversubscribed == _memoryPressureState) { - if (!object->canDemote()) { - continue; - } - object->demote(); - } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!object->canPromote()) { - continue; - } - object->promote(); - } else if (MemoryPressureState::Transfer == _memoryPressureState) { - if (!object->hasPendingTransfers()) { - continue; - } - object->executeNextTransfer(texture); - } else { - Q_UNREACHABLE(); - } - - // Reinject into the queue if more work to be done - addToWorkQueue(texture); - break; - } - - if (workQueue.empty()) { - _memoryPressureStateStale = true; - } -} - -void GL45VariableAllocationTexture::manageMemory() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); -} - -size_t GL45VariableAllocationTexture::_frameTexturesCreated { 0 }; GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { ++_frameTexturesCreated; } GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { - _memoryPressureStateStale = true; Backend::updateTextureGPUMemoryUsage(_size, 0); } -void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { - if (_populatedMip <= _allocatedMip) { - return; - } - - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - if (!_pendingTransfers.empty()) { - // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture - _currentTransferTexture = currentTexture; - if (_pendingTransfers.front()->tryTransfer()) { - _pendingTransfers.pop(); - _currentTransferTexture.reset(); - } - } -} - // Managed size resource textures using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; @@ -453,7 +53,6 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); allocateStorage(allocatedMip); - _memoryPressureStateStale = true; copyMipsFromTexture(); syncSampler(); @@ -521,7 +120,6 @@ void GL45ResourceTexture::promote() { glDeleteTextures(1, &oldId); // update the memory usage Backend::updateTextureGPUMemoryUsage(oldSize, 0); - _memoryPressureStateStale = true; syncSampler(); populateTransferQueue(); } @@ -554,7 +152,6 @@ void GL45ResourceTexture::demote() { glDeleteTextures(1, &oldId); // update the memory usage Backend::updateTextureGPUMemoryUsage(oldSize, 0); - _memoryPressureStateStale = true; syncSampler(); populateTransferQueue(); } diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index f28c18eb11..ce95e8657e 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -87,6 +87,7 @@ namespace gpu { class Sampler; class Texture; using TexturePointer = std::shared_ptr; + using TextureWeakPointer = std::weak_ptr; using Textures = std::vector; class TextureView; using TextureViews = std::vector; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index c274d69af6..309182b25e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -231,8 +231,6 @@ void Model::updateRenderItems() { // We need to update them here so we can correctly update the bounding box. self->updateClusterMatrices(); - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; render::Transaction transaction; @@ -266,7 +264,7 @@ void Model::updateRenderItems() { }); } - scene->enqueueTransaction(transaction); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); }); } @@ -534,7 +532,7 @@ void Model::calculateTriangleSets() { } } -void Model::setVisibleInScene(bool newValue, std::shared_ptr scene) { +void Model::setVisibleInScene(bool newValue, const render::ScenePointer& scene) { if (_isVisible != newValue) { _isVisible = newValue; @@ -550,7 +548,7 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen } -void Model::setLayeredInFront(bool layered, std::shared_ptr scene) { +void Model::setLayeredInFront(bool layered, const render::ScenePointer& scene) { if (_isLayeredInFront != layered) { _isLayeredInFront = layered; @@ -565,7 +563,7 @@ void Model::setLayeredInFront(bool layered, std::shared_ptr scene } } -bool Model::addToScene(std::shared_ptr scene, +bool Model::addToScene(const render::ScenePointer& scene, render::Transaction& transaction, render::Item::Status::Getters& statusGetters) { bool readyToRender = _collisionGeometry || isLoaded(); @@ -622,7 +620,7 @@ bool Model::addToScene(std::shared_ptr scene, return somethingAdded; } -void Model::removeFromScene(std::shared_ptr scene, render::Transaction& transaction) { +void Model::removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction) { foreach (auto item, _modelMeshRenderItemsMap.keys()) { transaction.removeItem(item); } @@ -795,7 +793,7 @@ void Model::setURL(const QUrl& url) { { render::Transaction transaction; - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene(); if (scene) { removeFromScene(scene, transaction); scene->enqueueTransaction(transaction); @@ -1271,7 +1269,7 @@ bool Model::isRenderable() const { return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty()); } -bool Model::initWhenReady(render::ScenePointer scene) { +bool Model::initWhenReady(const render::ScenePointer& scene) { // NOTE: this only called by SkeletonModel if (_addedToScene || !isRenderable()) { return false; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e852358bc4..62e045a3c1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -81,21 +81,21 @@ public: const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support - void setVisibleInScene(bool newValue, std::shared_ptr scene); - void setLayeredInFront(bool layered, std::shared_ptr scene); + void setVisibleInScene(bool newValue, const render::ScenePointer& scene); + void setLayeredInFront(bool layered, const render::ScenePointer& scene); bool needsFixupInScene() const; bool needsReload() const { return _needsReload; } - bool initWhenReady(render::ScenePointer scene); - bool addToScene(std::shared_ptr scene, + bool initWhenReady(const render::ScenePointer& scene); + bool addToScene(const render::ScenePointer& scene, render::Transaction& transaction) { auto getters = render::Item::Status::Getters(0); return addToScene(scene, transaction, getters); } - bool addToScene(std::shared_ptr scene, + bool addToScene(const render::ScenePointer& scene, render::Transaction& transaction, render::Item::Status::Getters& statusGetters); - void removeFromScene(std::shared_ptr scene, render::Transaction& transaction); + void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction); bool isRenderable() const; bool isVisible() const { return _isVisible; } diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index e744e83ebb..60e40c76f5 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -410,40 +410,36 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } void TabletProxy::pushOntoStack(const QVariant& path) { - if (_qmlTabletRoot) { - auto stack = _qmlTabletRoot->findChild("stack"); + QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow->asQuickItem(); + } + + if (root) { + auto stack = root->findChild("stack"); if (stack) { QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path)); } else { loadQMLSource(path); } - } else if (_desktopWindow) { - auto stack = _desktopWindow->asQuickItem()->findChild("stack"); - if (stack) { - QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path)); - } else { - qCDebug(scriptengine) << "tablet cannot push QML because _desktopWindow doesn't have child stack"; - } } else { qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } } void TabletProxy::popFromStack() { - if (_qmlTabletRoot) { - auto stack = _qmlTabletRoot->findChild("stack"); - if (stack) { - QMetaObject::invokeMethod(stack, "popSource"); - } else { - qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot doesn't have child stack"; - } - } else if (_desktopWindow) { - auto stack = _desktopWindow->asQuickItem()->findChild("stack"); - if (stack) { - QMetaObject::invokeMethod(stack, "popSource"); - } else { - qCDebug(scriptengine) << "tablet cannot pop QML because _desktopWindow doesn't have child stack"; - } + QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow->asQuickItem(); + } + + if (root) { + auto stack = root->findChild("stack"); + QMetaObject::invokeMethod(stack, "popSource"); } else { qCDebug(scriptengine) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null"; } diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 50722c0deb..10a9a20287 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -23,6 +23,10 @@ class AABox; class OctreeRenderer; +namespace render { + class Scene; + using ScenePointer = std::shared_ptr; +} namespace gpu { class Batch; @@ -126,6 +130,8 @@ public: bool _enableTexturing { true }; RenderDetails _details; + render::ScenePointer _scene; // HACK + int8_t _cameraMode { -1 }; // HACK }; #endif // hifi_RenderArgs_h diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index c3ad59bf48..20f994e63d 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -109,8 +109,12 @@ void MainWindow::changeEvent(QEvent* event) { stateChangeEvent->oldState() == Qt::WindowMaximized) && windowState() == Qt::WindowMinimized) { emit windowShown(false); + emit windowMinimizedChanged(true); } else { emit windowShown(true); + if (stateChangeEvent->oldState() == Qt::WindowMinimized) { + emit windowMinimizedChanged(false); + } } } else if (event->type() == QEvent::ActivationChange) { if (isActiveWindow()) { diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h index db02bfa1b6..75421340a2 100644 --- a/libraries/ui/src/MainWindow.h +++ b/libraries/ui/src/MainWindow.h @@ -29,6 +29,7 @@ public slots: signals: void windowGeometryChanged(QRect geometry); void windowShown(bool shown); + void windowMinimizedChanged(bool minimized); protected: virtual void closeEvent(QCloseEvent* event) override; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5806dd803a..f1a2e7bd08 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -75,7 +75,7 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // - +var linearTimeScale = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified @@ -2524,7 +2524,7 @@ function MyController(hand) { this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); - + this.linearTimeScale = timeScale; this.actionID = NULL_UUID; this.actionID = Entities.addAction("far-grab", this.grabbedThingID, { targetPosition: this.currentObjectPosition, @@ -2574,7 +2574,6 @@ function MyController(hand) { var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); - var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -2622,11 +2621,9 @@ function MyController(hand) { if (this.grabRadius < MINIMUM_GRAB_RADIUS) { this.grabRadius = MINIMUM_GRAB_RADIUS; } - var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition); - var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position); var handControllerData = getEntityCustomData('handControllerKey', this.grabbedThingID, defaultMoveWithHeadData); if (handControllerData.disableMoveWithHead !== true) { @@ -2659,9 +2656,13 @@ function MyController(hand) { this.grabbedThingID); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); + this.linearTimeScale = (this.linearTimeScale / 2); + if (this.linearTimeScale <= DISTANCE_HOLDING_ACTION_TIMEFRAME) { + this.linearTimeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; + } var success = Entities.updateAction(this.grabbedThingID, this.actionID, { targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), + linearTimeScale: this.linearTimeScale, targetRotation: this.currentObjectRotation, angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), ttl: ACTION_TTL diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 5fbea90025..ae64065216 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -400,6 +400,7 @@ function getProfilePicture(username, callback) { // callback(url) if successfull var matched = !error && html.match(/img class="users-img" src="([^"]*)"/); if (!matched) { print('Error: Unable to get profile picture for', username, error); + callback(''); return; } callback(matched[1]);