From 3f52d237edbf2cc7d810782f8b4c623d6d5e200f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 31 Mar 2016 10:27:17 -0700 Subject: [PATCH 01/23] adding Bullet profiler instances for better stats --- libraries/physics/src/EntityMotionState.cpp | 2 ++ libraries/physics/src/PhysicsEngine.cpp | 1 + .../physics/src/ThreadSafeDynamicsWorld.cpp | 26 ++++++++++++++++++- .../physics/src/ThreadSafeDynamicsWorld.h | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e3952ba1d6..7b83c2e2ce 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -175,11 +175,13 @@ bool EntityMotionState::isMoving() const { // (2) at the beginning of each simulation step for KINEMATIC RigidBody's -- // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { + BT_PROFILE("getWorldTransform"); if (!_entity) { return; } assert(entityTreeIsLocked()); if (_motionType == MOTION_TYPE_KINEMATIC) { + BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation. uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 0040c19c3d..74f6f90900 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -240,6 +240,7 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); if (_myAvatarController) { + BT_PROFILE("avatarController"); // TODO: move this stuff outside and in front of stepSimulation, because // the updateShapeIfNecessary() call needs info from MyAvatar and should // be done on the main thread during the pre-simulation stuff diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index b6f3487f1a..5fe99f137c 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -67,7 +67,10 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep saveKinematicState(fixedTimeStep*clampedSimulationSteps); - applyGravity(); + { + BT_PROFILE("applyGravity"); + applyGravity(); + } for (int i=0;igetActivationState() != ISLAND_SLEEPING) + { + if (body->isKinematicObject()) + { + //to calculate velocities next frame + body->saveKinematicState(timeStep); + } + } + } +} + + diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index e9708149da..68062d8d29 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -41,6 +41,7 @@ public: btScalar fixedTimeStep = btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); virtual void synchronizeMotionStates() override; + virtual void saveKinematicState(btScalar timeStep) override; // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide From 442b52313fbee5398df9311b35c234f06ba5ec2b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Apr 2016 12:02:01 -0700 Subject: [PATCH 02/23] faster kinematic motion calculations --- libraries/entities/src/EntityItem.cpp | 139 +++++++-------------- libraries/shared/src/SpatiallyNestable.cpp | 50 ++++++-- libraries/shared/src/SpatiallyNestable.h | 7 ++ 3 files changed, 93 insertions(+), 103 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 431d638063..69614d0d12 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -876,123 +876,76 @@ void EntityItem::simulate(const quint64& now) { } void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { -#ifdef WANT_DEBUG - qCDebug(entities) << "EntityItem::simulateKinematicMotion timeElapsed" << timeElapsed; -#endif - - const float MIN_TIME_SKIP = 0.0f; - const float MAX_TIME_SKIP = 1.0f; // in seconds - - timeElapsed = glm::clamp(timeElapsed, MIN_TIME_SKIP, MAX_TIME_SKIP); - - if (hasActions()) { + if (hasActions() || timeElapsed < 0.0f) { return; } - if (hasLocalAngularVelocity()) { - glm::vec3 localAngularVelocity = getLocalAngularVelocity(); + const float MAX_TIME_ELAPSED = 1.0f; // seconds + timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED); + Transform transform; + glm::vec3 linearVelocity; + glm::vec3 angularVelocity; + getLocalEverything(transform, linearVelocity, angularVelocity); + + bool isMoving = false; + if (glm::length2(angularVelocity) > 0.0f) { // angular damping if (_angularDamping > 0.0f) { - localAngularVelocity *= powf(1.0f - _angularDamping, timeElapsed); - #ifdef WANT_DEBUG - qCDebug(entities) << " angularDamping :" << _angularDamping; - qCDebug(entities) << " newAngularVelocity:" << localAngularVelocity; - #endif + angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); } - float angularSpeed = glm::length(localAngularVelocity); - - const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec - if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { - if (setFlags && angularSpeed > 0.0f) { - _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; - } - localAngularVelocity = ENTITY_ITEM_ZERO_VEC3; + const float EPSILON_ANGULAR_VELOCITY_LENGTH_SQUARED = 0.0017453f * 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec + if (glm::length2(angularVelocity) < EPSILON_ANGULAR_VELOCITY_LENGTH_SQUARED) { + angularVelocity = ENTITY_ITEM_ZERO_VEC3; } else { // for improved agreement with the way Bullet integrates rotations we use an approximation // and break the integration into bullet-sized substeps - glm::quat rotation = getRotation(); + glm::quat rotation = transform.getRotation(); float dt = timeElapsed; - while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) { - glm::quat dQ = computeBulletRotationStep(localAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP); + while (dt > 0.0f) { + glm::quat dQ = computeBulletRotationStep(angularVelocity, glm::min(dt, PHYSICS_ENGINE_FIXED_SUBSTEP)); rotation = glm::normalize(dQ * rotation); dt -= PHYSICS_ENGINE_FIXED_SUBSTEP; } - // NOTE: this final partial substep can drift away from a real Bullet simulation however - // it only becomes significant for rapidly rotating objects - // (e.g. around PI/4 radians per substep, or 7.5 rotations/sec at 60 substeps/sec). - glm::quat dQ = computeBulletRotationStep(localAngularVelocity, dt); - rotation = glm::normalize(dQ * rotation); - - setRotation(rotation); + transform.setRotation(rotation); + isMoving = true; } - - setLocalAngularVelocity(localAngularVelocity); } - - if (hasLocalVelocity()) { - - // acceleration is in the global frame, so transform it into the local frame. - // TODO: Move this into SpatiallyNestable. - bool success; - Transform transform = getParentTransform(success); - glm::vec3 localAcceleration(glm::vec3::_null); - if (success) { - localAcceleration = glm::inverse(transform.getRotation()) * getAcceleration(); - } else { - localAcceleration = getAcceleration(); - } - + if (glm::length2(linearVelocity) > 0.0f) { // linear damping - glm::vec3 localVelocity = getLocalVelocity(); if (_damping > 0.0f) { - localVelocity *= powf(1.0f - _damping, timeElapsed); - #ifdef WANT_DEBUG - qCDebug(entities) << " damping:" << _damping; - qCDebug(entities) << " velocity AFTER dampingResistance:" << localVelocity; - qCDebug(entities) << " glm::length(velocity):" << glm::length(localVelocity); - #endif + linearVelocity *= powf(1.0f - _damping, timeElapsed); } - // integrate position forward - glm::vec3 localPosition = getLocalPosition(); - glm::vec3 newLocalPosition = localPosition + (localVelocity * timeElapsed) + 0.5f * localAcceleration * timeElapsed * timeElapsed; - - #ifdef WANT_DEBUG - qCDebug(entities) << " EntityItem::simulate()...."; - qCDebug(entities) << " timeElapsed:" << timeElapsed; - qCDebug(entities) << " old AACube:" << getMaximumAACube(); - qCDebug(entities) << " old position:" << localPosition; - qCDebug(entities) << " old velocity:" << localVelocity; - qCDebug(entities) << " old getAABox:" << getAABox(); - qCDebug(entities) << " newPosition:" << newPosition; - qCDebug(entities) << " glm::distance(newPosition, position):" << glm::distance(newLocalPosition, localPosition); - #endif - - localPosition = newLocalPosition; - - // apply effective acceleration, which will be the same as gravity if the Entity isn't at rest. - localVelocity += localAcceleration * timeElapsed; - - float speed = glm::length(localVelocity); - const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec - if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { - setVelocity(ENTITY_ITEM_ZERO_VEC3); - if (setFlags && speed > 0.0f) { - _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; + glm::vec3 linearAcceleration = _acceleration; + if (glm::length2(_acceleration) > 0.0f) { + // acceleration is in world-frame but we need it in local-frame + bool success; + Transform parentTransform = getParentTransform(success); + if (success) { + linearAcceleration = glm::inverse(parentTransform.getRotation()) * linearAcceleration; } - } else { - setLocalPosition(localPosition); - setLocalVelocity(localVelocity); } - #ifdef WANT_DEBUG - qCDebug(entities) << " new position:" << position; - qCDebug(entities) << " new velocity:" << velocity; - qCDebug(entities) << " new AACube:" << getMaximumAACube(); - qCDebug(entities) << " old getAABox:" << getAABox(); - #endif + // integrate linearVelocity + linearVelocity += linearAcceleration * timeElapsed; + + const float EPSILON_LINEAR_VELOCITY_LENGTH_SQUARED = 1.0e-6f; // 1mm/sec ^2 + if (glm::length2(linearVelocity) < EPSILON_LINEAR_VELOCITY_LENGTH_SQUARED) { + setVelocity(ENTITY_ITEM_ZERO_VEC3); + } else { + // integrate position forward + // NOTE: we're using the NEW linear velocity, which is why we negate the acceleration term + glm::vec3 position = transform.getTranslation() + (linearVelocity * timeElapsed) - 0.5f * linearAcceleration * timeElapsed * timeElapsed; + transform.setTranslation(position); + isMoving = true; + } + } + setLocalEverything(transform, linearVelocity, angularVelocity); + if (!isMoving) { + // flag this entity to be removed from kinematic motion + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 13bf5d9054..550864265f 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -90,11 +90,9 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons return parent; } - SpatiallyNestablePointer thisPointer = getThisPointer(); - if (parent) { // we have a parent pointer but our _parentID doesn't indicate this parent. - parent->forgetChild(thisPointer); + parent->forgetChild(getThisPointer()); _parentKnowsMe = false; _parent.reset(); } @@ -112,16 +110,11 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons parent = _parent.lock(); if (parent) { - parent->beParentOfChild(thisPointer); + parent->beParentOfChild(getThisPointer()); _parentKnowsMe = true; } - if (parent || parentID.isNull()) { - success = true; - } else { - success = false; - } - + success = (parent || parentID.isNull()); return parent; } @@ -849,3 +842,40 @@ AACube SpatiallyNestable::getQueryAACube() const { } return result; } + +void SpatiallyNestable::getLocalEverything( + Transform& transform, + glm::vec3& velocity, + glm::vec3& angularVelocity) const { + // transform + _transformLock.withReadLock([&] { + transform = _transform; + }); + // linear velocity + _velocityLock.withReadLock([&] { + velocity = _velocity; + }); + // angular velocity + _angularVelocityLock.withReadLock([&] { + angularVelocity = _angularVelocity; + }); +} + +void SpatiallyNestable::setLocalEverything( + const Transform& localTransform, + const glm::vec3& localVelocity, + const glm::vec3& localAngularVelocity) { + // transform + _transformLock.withWriteLock([&] { + _transform = localTransform; + }); + // linear velocity + _velocityLock.withWriteLock([&] { + _velocity = localVelocity; + }); + // angular velocity + _angularVelocityLock.withWriteLock([&] { + _angularVelocity = localAngularVelocity; + }); + locationChanged(); +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 379f2facd7..6546d46e51 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -149,6 +149,13 @@ protected: quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? SpatiallyNestablePointer getParentPointer(bool& success) const; + void getLocalEverything(Transform& localTransform, glm::vec3& localVelocity, glm::vec3& localAngularVelocity) const; + + void setLocalEverything( + const Transform& localTransform, + const glm::vec3& localVelocity, + const glm::vec3& localAngularVelocity); + mutable SpatiallyNestableWeakPointer _parent; virtual void beParentOfChild(SpatiallyNestablePointer newChild) const; From e1602b57faad3e92c2c3862bda45016587882719 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Apr 2016 12:03:21 -0700 Subject: [PATCH 03/23] faster isNaN checks --- libraries/shared/src/GLMHelpers.cpp | 8 -------- libraries/shared/src/GLMHelpers.h | 4 ++-- libraries/shared/src/SharedUtil.cpp | 17 +---------------- libraries/shared/src/SharedUtil.h | 6 +++--- libraries/shared/src/Transform.cpp | 4 ---- libraries/shared/src/Transform.h | 2 +- 6 files changed, 7 insertions(+), 34 deletions(-) diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index d21d88d212..53abb3827d 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -463,14 +463,6 @@ glm::vec2 getFacingDir2D(const glm::mat4& m) { } } -bool isNaN(glm::vec3 value) { - return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); -} - -bool isNaN(glm::quat value) { - return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); -} - glm::mat4 orthoInverse(const glm::mat4& m) { glm::mat4 r = m; r[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 469ca1fc81..8b1446d4e5 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -229,8 +229,8 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda glm::vec2 getFacingDir2D(const glm::quat& rot); glm::vec2 getFacingDir2D(const glm::mat4& m); -bool isNaN(glm::vec3 value); -bool isNaN(glm::quat value); +inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); } +inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); } glm::mat4 orthoInverse(const glm::mat4& m); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 30d4726bcc..d48bddbd88 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -247,12 +247,6 @@ int getNthBit(unsigned char byte, int ordinal) { return ERROR_RESULT; } -bool isBetween(int64_t value, int64_t max, int64_t min) { - return ((value <= max) && (value >= min)); -} - - - void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { //assert(value <= 3 && value >= 0); byte |= ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 @@ -260,12 +254,7 @@ void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { bool isInEnvironment(const char* environment) { char* environmentString = getenv("HIFI_ENVIRONMENT"); - - if (environmentString && strcmp(environmentString, environment) == 0) { - return true; - } else { - return false; - } + return (environmentString && strcmp(environmentString, environment) == 0); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -632,10 +621,6 @@ void debug::checkDeadBeef(void* memoryVoid, int size) { assert(memcmp((unsigned char*)memoryVoid, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0); } -bool isNaN(float value) { - return value != value; -} - QString formatUsecTime(float usecs, int prec) { static const quint64 SECONDS_PER_MINUTE = 60; static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 8fb65a5247..e9201b4a92 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -180,11 +180,11 @@ private: static int DEADBEEF_SIZE; }; -bool isBetween(int64_t value, int64_t max, int64_t min); - +/// \return true when value is between max and min +inline bool isBetween(int64_t value, int64_t max, int64_t min) { return ((value <= max) && (value >= min)); } /// \return bool is the float NaN -bool isNaN(float value); +inline bool isNaN(float value) { return value != value; } QString formatUsecTime(float usecs, int prec = 3); QString formatSecondsElapsed(float seconds); diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index a3a3c05731..c51b3dae4b 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -150,7 +150,3 @@ QJsonObject Transform::toJson(const Transform& transform) { } return result; } - -bool Transform::containsNaN() const { - return isNaN(_rotation) || isNaN(_scale) || isNaN(_translation); -} diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 1024173cbd..1e1d10c54b 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -145,7 +145,7 @@ public: Vec4 transform(const Vec4& pos) const; Vec3 transform(const Vec3& pos) const; - bool containsNaN() const; + bool containsNaN() const { return isNaN(_rotation) || isNaN(glm::dot(_scale, _translation)); } protected: From 25fbf926df007496267efcc3c1b97331a430834a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Apr 2016 12:04:27 -0700 Subject: [PATCH 04/23] CollisionWorld only updates _active_ Aabbs we manually set/clear active state of static objects that need their Aabbs updated also fixing a bug when starting kinematic motion --- libraries/physics/src/EntityMotionState.cpp | 17 ++++++--------- libraries/physics/src/EntityMotionState.h | 6 +----- libraries/physics/src/ObjectMotionState.cpp | 18 ++++++++++++---- libraries/physics/src/ObjectMotionState.h | 10 +++++---- libraries/physics/src/PhysicsEngine.cpp | 24 ++++++++++++++++----- libraries/physics/src/PhysicsEngine.h | 6 +++--- 6 files changed, 50 insertions(+), 31 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 7b83c2e2ce..5f168d2e33 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -94,7 +94,7 @@ void EntityMotionState::updateServerPhysicsVariables() { } // virtual -bool EntityMotionState::handleEasyChanges(uint32_t& flags) { +void EntityMotionState::handleEasyChanges(uint32_t& flags) { assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); @@ -137,8 +137,6 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) { if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { _body->activate(); } - - return true; } @@ -422,19 +420,18 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; if (accVsGravity < ACCELERATION_EQUIVALENT_EPSILON_RATIO * gravityLength) { // acceleration measured during the most recent simulation step was close to gravity. - if (getAccelerationNearlyGravityCount() < STEPS_TO_DECIDE_BALLISTIC) { - // only increment this if we haven't reached the threshold yet. this is to avoid - // overflowing the counter. - incrementAccelerationNearlyGravityCount(); + if (_accelerationNearlyGravityCount < STEPS_TO_DECIDE_BALLISTIC) { + // only increment this if we haven't reached the threshold yet, to avoid overflowing the counter + ++_accelerationNearlyGravityCount; } } else { - // acceleration wasn't similar to this entities gravity, so reset the went-ballistic counter - resetAccelerationNearlyGravityCount(); + // acceleration wasn't similar to this entity's gravity, reset the counter + _accelerationNearlyGravityCount = 0; } // if this entity has been accelerated at close to gravity for a certain number of simulation-steps, let // the entity server's estimates include gravity. - if (getAccelerationNearlyGravityCount() >= STEPS_TO_DECIDE_BALLISTIC) { + if (_accelerationNearlyGravityCount >= STEPS_TO_DECIDE_BALLISTIC) { _entity->setAcceleration(_entity->getGravity()); } else { _entity->setAcceleration(glm::vec3(0.0f)); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index ac16ec6d5d..eb42d03b52 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,7 +29,7 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); - virtual bool handleEasyChanges(uint32_t& flags) override; + virtual void handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; /// \return PhysicsMotionType based on params set in EntityItem @@ -51,10 +51,6 @@ public: virtual uint32_t getIncomingDirtyFlags() override; virtual void clearIncomingDirtyFlags() override; - void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } - void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } - uint8_t getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } - virtual float getObjectRestitution() const override { return _entity->getRestitution(); } virtual float getObjectFriction() const override { return _entity->getFriction(); } virtual float getObjectLinearDamping() const override { return _entity->getDamping(); } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 482c3146f8..6b2022e204 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -39,7 +39,7 @@ const glm::vec3& ObjectMotionState::getWorldOffset() { } // static -uint32_t worldSimulationStep = 0; +uint32_t worldSimulationStep = 1; void ObjectMotionState::setWorldSimulationStep(uint32_t step) { assert(step > worldSimulationStep); worldSimulationStep = step; @@ -164,7 +164,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } -bool ObjectMotionState::handleEasyChanges(uint32_t& flags) { +void ObjectMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); btVector3 newPosition = glmToBullet(getObjectPosition()); @@ -183,6 +183,10 @@ bool ObjectMotionState::handleEasyChanges(uint32_t& flags) { worldTrans.setRotation(newRotation); } _body->setWorldTransform(worldTrans); + if (!(flags & HARD_DIRTY_PHYSICS_FLAGS) && _body->isStaticObject()) { + // force activate static body so its Aabb is updated later + _body->activate(true); + } } else if (flags & Simulation::DIRTY_ROTATION) { btTransform worldTrans = _body->getWorldTransform(); btQuaternion newRotation = glmToBullet(getObjectRotation()); @@ -192,6 +196,10 @@ bool ObjectMotionState::handleEasyChanges(uint32_t& flags) { } worldTrans.setRotation(newRotation); _body->setWorldTransform(worldTrans); + if (!(flags & HARD_DIRTY_PHYSICS_FLAGS) && _body->isStaticObject()) { + // force activate static body so its Aabb is updated later + _body->activate(true); + } } if (flags & Simulation::DIRTY_LINEAR_VELOCITY) { @@ -232,8 +240,6 @@ bool ObjectMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_MASS) { updateBodyMassProperties(); } - - return true; } bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { @@ -292,6 +298,10 @@ void ObjectMotionState::updateBodyVelocities() { _body->setActivationState(ACTIVE_TAG); } +void ObjectMotionState::updateLastKinematicStep() { + _lastKinematicStep = ObjectMotionState::getWorldSimulationStep() - 1; +} + void ObjectMotionState::updateBodyMassProperties() { float mass = getMass(); btVector3 inertia(0.0f, 0.0f, 0.0f); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index bb78eb12d6..44cb88721c 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -50,11 +50,12 @@ const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TY Simulation::DIRTY_COLLISION_GROUP); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | - Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY); + Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY | + Simulation::DIRTY_PHYSICS_ACTIVATION); + // These are the set of incoming flags that the PhysicsEngine needs to hear about: -const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | - Simulation::DIRTY_PHYSICS_ACTIVATION); +const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS); // These are the outgoing flags that the PhysicsEngine can affect: const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES; @@ -80,11 +81,12 @@ public: ObjectMotionState(btCollisionShape* shape); ~ObjectMotionState(); - virtual bool handleEasyChanges(uint32_t& flags); + virtual void handleEasyChanges(uint32_t& flags); virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); void updateBodyMaterialProperties(); void updateBodyVelocities(); + void updateLastKinematicStep(); virtual void updateBodyMassProperties(); MotionStateType getType() const { return _type; } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 74f6f90900..55861e9b3a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -50,6 +50,7 @@ void PhysicsEngine::init() { // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones _dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + _dynamicsWorld->setForceUpdateAllAabbs(false); } } @@ -80,6 +81,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); motionState->updateBodyVelocities(); + motionState->updateLastKinematicStep(); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD); @@ -189,12 +191,18 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob stillNeedChange.push_back(object); } } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - if (object->handleEasyChanges(flags)) { - object->clearIncomingDirtyFlags(); - } else { - stillNeedChange.push_back(object); - } + object->handleEasyChanges(flags); + object->clearIncomingDirtyFlags(); } + if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { + _activeStaticBodies.push_back(object->getRigidBody()); + } + } + // active static bodies have changed (in an Easy way) and need their Aabbs updated + // but we've configured Bullet to NOT update them automatically (for improved performance) + // so we must do it outselves + for (int i = 0; i < _activeStaticBodies.size(); ++i) { + _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]); } return stillNeedChange; } @@ -389,6 +397,12 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { const VectorOfMotionStates& PhysicsEngine::getOutgoingChanges() { BT_PROFILE("copyOutgoingChanges"); + // Bullet will not deactivate static objects (it doesn't expect them to be active) + // so we must deactivate them ourselves + for (int i = 0; i < _activeStaticBodies.size(); ++i) { + _activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING); + } + _activeStaticBodies.clear(); _dynamicsWorld->synchronizeMotionStates(); _hasOutgoingChanges = false; return _dynamicsWorld->getChangedMotionStates(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index f644d6f5b2..18e4016fec 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -13,9 +13,9 @@ #define hifi_PhysicsEngine_h #include +#include #include -#include #include #include @@ -41,7 +41,7 @@ public: }; typedef std::map ContactMap; -typedef QVector CollisionEvents; +typedef std::vector CollisionEvents; class PhysicsEngine { public: @@ -110,6 +110,7 @@ private: ContactMap _contactMap; CollisionEvents _collisionEvents; QHash _objectActions; + std::vector _activeStaticBodies; glm::vec3 _originOffset; QUuid _sessionID; @@ -121,7 +122,6 @@ private: bool _dumpNextStats = false; bool _hasOutgoingChanges = false; - }; typedef std::shared_ptr PhysicsEnginePointer; From de5fe705a33e171016b9855c9383c147620ba312 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Apr 2016 12:06:47 -0700 Subject: [PATCH 05/23] optimize Bullet broadphase using collision groups --- libraries/shared/src/PhysicsCollisionGroups.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index 6d320e69cb..794f338dc5 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -51,10 +51,10 @@ const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14; const int16_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS; // STATIC does not collide with itself (as optimization of physics simulation) -const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_STATIC); +const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_STATIC); const int16_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT; -const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_DEFAULT; +const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC; // MY_AVATAR does not collide with itself const int16_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR); From 20914df3303e79cd61c79a1c91222171e4c15956 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Apr 2016 13:31:14 -0700 Subject: [PATCH 06/23] fix warning about signed/unsigned comparison --- libraries/physics/src/PhysicsEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 55861e9b3a..973be0b4e8 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -201,7 +201,7 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob // active static bodies have changed (in an Easy way) and need their Aabbs updated // but we've configured Bullet to NOT update them automatically (for improved performance) // so we must do it outselves - for (int i = 0; i < _activeStaticBodies.size(); ++i) { + for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]); } return stillNeedChange; @@ -399,7 +399,7 @@ const VectorOfMotionStates& PhysicsEngine::getOutgoingChanges() { BT_PROFILE("copyOutgoingChanges"); // Bullet will not deactivate static objects (it doesn't expect them to be active) // so we must deactivate them ourselves - for (int i = 0; i < _activeStaticBodies.size(); ++i) { + for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { _activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING); } _activeStaticBodies.clear(); From 0830c55bcfacef6c6f2fa49ad636acae83b4ed8d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Apr 2016 17:48:56 -0700 Subject: [PATCH 07/23] getLocalEverything->getLocalTransformAndVelocities --- libraries/entities/src/EntityItem.cpp | 4 ++-- libraries/shared/src/SpatiallyNestable.cpp | 4 ++-- libraries/shared/src/SpatiallyNestable.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 69614d0d12..5c825495b9 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -886,7 +886,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { Transform transform; glm::vec3 linearVelocity; glm::vec3 angularVelocity; - getLocalEverything(transform, linearVelocity, angularVelocity); + getLocalTransformAndVelocities(transform, linearVelocity, angularVelocity); bool isMoving = false; if (glm::length2(angularVelocity) > 0.0f) { @@ -942,7 +942,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { isMoving = true; } } - setLocalEverything(transform, linearVelocity, angularVelocity); + setLocalTransformAndVelocities(transform, linearVelocity, angularVelocity); if (!isMoving) { // flag this entity to be removed from kinematic motion _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 550864265f..3cbd3c5ac9 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -843,7 +843,7 @@ AACube SpatiallyNestable::getQueryAACube() const { return result; } -void SpatiallyNestable::getLocalEverything( +void SpatiallyNestable::getLocalTransformAndVelocities( Transform& transform, glm::vec3& velocity, glm::vec3& angularVelocity) const { @@ -861,7 +861,7 @@ void SpatiallyNestable::getLocalEverything( }); } -void SpatiallyNestable::setLocalEverything( +void SpatiallyNestable::setLocalTransformAndVelocities( const Transform& localTransform, const glm::vec3& localVelocity, const glm::vec3& localAngularVelocity) { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 6546d46e51..ef70d0231b 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -149,9 +149,9 @@ protected: quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? SpatiallyNestablePointer getParentPointer(bool& success) const; - void getLocalEverything(Transform& localTransform, glm::vec3& localVelocity, glm::vec3& localAngularVelocity) const; + void getLocalTransformAndVelocities(Transform& localTransform, glm::vec3& localVelocity, glm::vec3& localAngularVelocity) const; - void setLocalEverything( + void setLocalTransformAndVelocities( const Transform& localTransform, const glm::vec3& localVelocity, const glm::vec3& localAngularVelocity); From 79e528633584f111a2c9a9fcdec07e8b798838d0 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 3 Apr 2016 11:53:59 -0700 Subject: [PATCH 08/23] Fix ImageReader threading issues --- .../src/model-networking/TextureCache.cpp | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3ba36dc2da..ff189b46f7 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -196,7 +196,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture { _textureLoader = textureLoader; } - + NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { switch (_type) { case CUBE_TEXTURE: { @@ -240,14 +240,14 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { class ImageReader : public QRunnable { public: - ImageReader(const QWeakPointer& texture, const QByteArray& data, const QUrl& url = QUrl()); + ImageReader(const QWeakPointer& resource, const QByteArray& data, const QUrl& url = QUrl()); virtual void run(); private: static void listSupportedImageFormats(); - QWeakPointer _texture; + QWeakPointer _resource; QUrl _url; QByteArray _content; }; @@ -261,9 +261,9 @@ void NetworkTexture::loadContent(const QByteArray& content) { QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url)); } -ImageReader::ImageReader(const QWeakPointer& texture, const QByteArray& data, +ImageReader::ImageReader(const QWeakPointer& resource, const QByteArray& data, const QUrl& url) : - _texture(texture), + _resource(resource), _url(url), _content(data) { @@ -284,25 +284,25 @@ void ImageReader::run() { } QThread::currentThread()->setPriority(QThread::LowPriority); - auto texture = _texture.toStrongRef(); - if (!texture) { - qCWarning(modelnetworking) << "Could not get strong ref"; + if (!_resource.data()) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + QThread::currentThread()->setPriority(originalPriority); return; } listSupportedImageFormats(); - // try to help the QImage loader by extracting the image file format from the url filename ext - // Some tga are not created properly for example without it + // Help the QImage loader by extracting the image file format from the url filename ext. + // Some tga are not created properly without it. auto filename = _url.fileName().toStdString(); auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); QImage image = QImage::fromData(_content, filenameExtension.c_str()); // Note that QImage.format is the pixel format which is different from the "format" of the image file... - auto imageFormat = image.format(); + auto imageFormat = image.format(); int originalWidth = image.width(); int originalHeight = image.height(); - + if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) { if (filenameExtension.empty()) { qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url; @@ -312,15 +312,31 @@ void ImageReader::run() { return; } - gpu::Texture* theTexture = nullptr; - auto ntex = texture.dynamicCast(); - if (ntex) { - theTexture = ntex->getTextureLoader()(image, _url.toString().toStdString()); + gpu::Texture* texture = nullptr; + { + // Double-check the resource still exists between long operations. + auto resource = _resource.toStrongRef(); + if (!resource) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + QThread::currentThread()->setPriority(originalPriority); + return; + } + + auto url = _url.toString().toStdString(); + texture = resource.dynamicCast()->getTextureLoader()(image, url); + } + + // Ensure the resource has not been deleted, and won't be while invokeMethod is in flight. + auto resource = _resource.toStrongRef(); + if (!resource) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + delete texture; + } else { + QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection, + Q_ARG(void*, texture), + Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); } - QMetaObject::invokeMethod(texture.data(), "setImage", - Q_ARG(void*, theTexture), - Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); QThread::currentThread()->setPriority(originalPriority); } @@ -328,9 +344,9 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth, int originalHeight) { _originalWidth = originalWidth; _originalHeight = originalHeight; - + gpu::Texture* texture = static_cast(voidTexture); - + // Passing ownership _textureSource->resetTexture(texture); auto gpuTexture = _textureSource->getGPUTexture(); From 7ea81f39379105104e8e3505b31025599c42dbe6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 3 Apr 2016 19:15:01 -0700 Subject: [PATCH 09/23] add and fix comments --- libraries/physics/src/ObjectMotionState.cpp | 5 ++++- libraries/physics/src/PhysicsEngine.cpp | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 6b2022e204..e902758461 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -38,8 +38,11 @@ const glm::vec3& ObjectMotionState::getWorldOffset() { return _worldOffset; } +// We init worldSimulationStep to 1 instead of 0 because we initialize _lastKineticStep to (worldSimulationStep - 1) +// so that the object starts moving on the first frame that it was set kinematic. +static uint32_t worldSimulationStep { 1 }; + // static -uint32_t worldSimulationStep = 1; void ObjectMotionState::setWorldSimulationStep(uint32_t step) { assert(step > worldSimulationStep); worldSimulationStep = step; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 973be0b4e8..be80149c96 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -50,6 +50,12 @@ void PhysicsEngine::init() { // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones _dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + + // By default Bullet will update the Aabb's of all objects every frame, even statics. + // This can waste CPU cycles so we configure Bullet to only update ACTIVE objects here. + // However, this means when a static object is moved we must manually update its Aabb + // in order for its broadphase collision queries to work correctly. Look at how we use + // _activeStaticBodies to track and update the Aabb's of moved static objects. _dynamicsWorld->setForceUpdateAllAabbs(false); } } @@ -200,7 +206,7 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob } // active static bodies have changed (in an Easy way) and need their Aabbs updated // but we've configured Bullet to NOT update them automatically (for improved performance) - // so we must do it outselves + // so we must do it ourselves for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]); } From 74058ac049336dbdf7a332b6458be7ee68b7e8f5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 3 Apr 2016 19:15:16 -0700 Subject: [PATCH 10/23] more correct moving test for ballistic kinematics --- libraries/entities/src/EntityItem.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5c825495b9..5b9791ca7b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -919,7 +919,8 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { } glm::vec3 linearAcceleration = _acceleration; - if (glm::length2(_acceleration) > 0.0f) { + bool nonZeroAcceleration = (glm::length2(_acceleration) > 0.0f); + if (nonZeroAcceleration) { // acceleration is in world-frame but we need it in local-frame bool success; Transform parentTransform = getParentTransform(success); @@ -928,17 +929,20 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { } } + // integrate position forward + glm::vec3 position = transform.getTranslation() + (linearVelocity * timeElapsed) + 0.5f * linearAcceleration * timeElapsed * timeElapsed; + transform.setTranslation(position); + // integrate linearVelocity linearVelocity += linearAcceleration * timeElapsed; const float EPSILON_LINEAR_VELOCITY_LENGTH_SQUARED = 1.0e-6f; // 1mm/sec ^2 if (glm::length2(linearVelocity) < EPSILON_LINEAR_VELOCITY_LENGTH_SQUARED) { setVelocity(ENTITY_ITEM_ZERO_VEC3); + if (nonZeroAcceleration) { + isMoving = true; + } } else { - // integrate position forward - // NOTE: we're using the NEW linear velocity, which is why we negate the acceleration term - glm::vec3 position = transform.getTranslation() + (linearVelocity * timeElapsed) - 0.5f * linearAcceleration * timeElapsed * timeElapsed; - transform.setTranslation(position); isMoving = true; } } From d98abbc7df5d05b72a29a5015c81445df4f89499 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 3 Apr 2016 01:04:05 -0700 Subject: [PATCH 11/23] First pass at threaded texture transfers --- interface/src/Application.cpp | 3 + libraries/gl/src/gl/GLHelpers.cpp | 4 +- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 5 + libraries/gl/src/gl/OffscreenGLCanvas.h | 1 + libraries/gl/src/gl/OglplusHelpers.cpp | 40 +- libraries/gl/src/gl/OglplusHelpers.h | 3 +- libraries/gl/src/gl/QOpenGLContextWrapper.cpp | 3 + libraries/gl/src/gl/QOpenGLContextWrapper.h | 3 +- libraries/gpu/src/gpu/GLBackend.cpp | 1 + libraries/gpu/src/gpu/GLBackend.h | 65 ++- libraries/gpu/src/gpu/GLBackendOutput.cpp | 4 +- libraries/gpu/src/gpu/GLBackendPipeline.cpp | 2 +- libraries/gpu/src/gpu/GLBackendTexture.cpp | 441 +++++++++--------- .../gpu/src/gpu/GLBackendTextureTransfer.cpp | 109 +++++ .../gpu/src/gpu/GLBackendTextureTransfer.h | 61 +++ libraries/gpu/src/gpu/GLTexelFormat.h | 3 + libraries/shared/src/GenericThread.cpp | 19 +- libraries/shared/src/GenericThread.h | 7 +- 18 files changed, 540 insertions(+), 234 deletions(-) create mode 100644 libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp create mode 100644 libraries/gpu/src/gpu/GLBackendTextureTransfer.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0172b3ce3a..80173b7d9d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1255,6 +1255,9 @@ void Application::initializeGL() { // Where the gpuContext is initialized and where the TRUE Backend is created and assigned gpu::Context::init(); _gpuContext = std::make_shared(); + // The gpu context can make child contexts for transfers, so + // we need to restore primary rendering context + _offscreenContext->makeCurrent(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 6ad7f816b8..c9de3ccd90 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -12,7 +12,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); - format.setVersion(4, 1); + format.setVersion(4, 5); #ifdef DEBUG format.setOption(QSurfaceFormat::DebugContext); #endif @@ -27,7 +27,7 @@ const QGLFormat& getDefaultGLFormat() { static QGLFormat glFormat; static std::once_flag once; std::call_once(once, [] { - glFormat.setVersion(4, 1); + glFormat.setVersion(4, 5); glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0 glFormat.setSampleBuffers(false); glFormat.setDepth(false); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 8e5579f90b..90ff369cd6 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -83,3 +83,8 @@ void OffscreenGLCanvas::doneCurrent() { QObject* OffscreenGLCanvas::getContextObject() { return _context; } + +void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) { + moveToThread(thread); + _context->moveToThread(thread); +} \ No newline at end of file diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 9858c7cefa..387804bf56 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -26,6 +26,7 @@ public: bool create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); + void moveToThreadWithContext(QThread* thread); QOpenGLContext* getContext() { return _context; } diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp index 1dd7068448..11c4f2fe3d 100644 --- a/libraries/gl/src/gl/OglplusHelpers.cpp +++ b/libraries/gl/src/gl/OglplusHelpers.cpp @@ -6,8 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OglplusHelpers.h" -#include + #include +#include +#include using namespace oglplus; using namespace oglplus::shapes; @@ -20,11 +22,13 @@ uniform mat4 mvp = mat4(1); in vec3 Position; in vec2 TexCoord; +out vec3 vPosition; out vec2 vTexCoord; void main() { gl_Position = mvp * vec4(Position, 1); - vTexCoord = TexCoord ; + vTexCoord = TexCoord; + vPosition = Position; } )VS"; @@ -35,7 +39,9 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core uniform sampler2D sampler; uniform float alpha = 1.0; +in vec3 vPosition; in vec2 vTexCoord; + out vec4 FragColor; void main() { @@ -47,12 +53,38 @@ void main() { )FS"; +static const char * SIMPLE_TEXTURED_CUBEMAP_FS = R"FS(#version 410 core +#pragma line __LINE__ + +uniform samplerCube sampler; +uniform float alpha = 1.0; + +in vec3 vPosition; +in vec3 vTexCoord; + +out vec4 FragColor; + +void main() { + + FragColor = texture(sampler, vPosition); + FragColor.a *= alpha; +} + +)FS"; + + ProgramPtr loadDefaultShader() { ProgramPtr result; compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS); return result; } +ProgramPtr loadCubemapShader() { + ProgramPtr result; + compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_CUBEMAP_FS); + return result; +} + void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) { using namespace oglplus; try { @@ -93,6 +125,10 @@ ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) { ); } +ShapeWrapperPtr loadSkybox(ProgramPtr program) { + return ShapeWrapperPtr(new shapes::ShapeWrapper({ { "Position" } }, shapes::SkyBox(), *program)); +} + // Return a point's cartesian coordinates on a sphere from pitch and yaw static glm::vec3 getPoint(float yaw, float pitch) { return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index 4734b8b213..b599e8270d 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -37,7 +37,6 @@ #include #include #include -#include #ifdef _WIN32 #pragma warning(pop) @@ -55,7 +54,9 @@ using ProgramPtr = std::shared_ptr; using Mat4Uniform = oglplus::Uniform; ProgramPtr loadDefaultShader(); +ProgramPtr loadCubemapShader(); void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs); +ShapeWrapperPtr loadSkybox(ProgramPtr program); ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f); ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32); diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 6397d30e13..185fdaf7f4 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -13,6 +13,9 @@ #include +QOpenGLContext* QOpenGLContextWrapper::currentContext() { + return QOpenGLContext::currentContext(); +} QOpenGLContextWrapper::QOpenGLContextWrapper() : _context(new QOpenGLContext) diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index b736253213..09f1d67280 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -19,7 +19,6 @@ class QSurfaceFormat; class QOpenGLContextWrapper { public: QOpenGLContextWrapper(); - void setFormat(const QSurfaceFormat& format); bool create(); void swapBuffers(QSurface* surface); @@ -27,6 +26,8 @@ public: void doneCurrent(); void setShareContext(QOpenGLContext* otherContext); + static QOpenGLContext* currentContext(); + QOpenGLContext* getContext() { return _context; } diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index b5b6437ed8..f51448f8fd 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -125,6 +125,7 @@ GLBackend::GLBackend() { glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); initInput(); initTransform(); + initTextureTransferHelper(); } GLBackend::~GLBackend() { diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f5abacd279..4f22a68631 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -24,6 +24,8 @@ namespace gpu { +class GLTextureTransferHelper; + class GLBackend : public Backend { // Context Backend static interface required @@ -35,7 +37,6 @@ class GLBackend : public Backend { explicit GLBackend(bool syncCache); GLBackend(); public: - virtual ~GLBackend(); virtual void render(Batch& batch); @@ -75,25 +76,63 @@ public: class GLTexture : public GPUObject { public: - Stamp _storageStamp; - Stamp _contentStamp; - GLuint _texture; - GLenum _target; + const Stamp _storageStamp; + Stamp _contentStamp { 0 }; + const GLuint _texture; + const GLenum _target; - GLTexture(); + GLTexture(const gpu::Texture& gpuTexture); ~GLTexture(); - void setSize(GLuint size); GLuint size() const { return _size; } + enum SyncState { + // The texture is currently undergoing no processing, although it's content + // may be out of date, or it's storage may be invalid relative to the + // owning GPU texture + Idle, + // The texture has been queued for transfer to the GPU + Pending, + // The texture has been transferred to the GPU, but is awaiting + // any post transfer operations that may need to occur on the + // primary rendering thread + Transferred, + }; + + void setSyncState(SyncState syncState) { _syncState = syncState; } + SyncState getSyncState() const { return _syncState; } + + // Is the storage out of date relative to the gpu texture? + bool invalid() const; + + // Is the content out of date relative to the gpu texture? + bool outdated() const; + + // Is the texture in a state where it can be rendered with no work? + bool ready() const; + + // Move the image bits from the CPU to the GPU + void transfer() const; + + // Execute any post-move operations that must occur only on the main thread + void postTransfer(); + + static const size_t CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[6]; + private: - GLuint _size; + void transferMip(GLenum target, const Texture::PixelsPointer& mip) const; + + const GLuint _size; + // The owning texture + const Texture& _gpuTexture; + std::atomic _syncState { SyncState::Idle }; }; - static GLTexture* syncGPUObject(const Texture& texture); + static GLTexture* syncGPUObject(const TexturePointer& texture); static GLuint getTextureID(const TexturePointer& texture, bool sync = true); // very specific for now - static void syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object); + static void syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object); class GLShader : public GPUObject { public: @@ -241,6 +280,11 @@ protected: void renderPassTransfer(Batch& batch); void renderPassDraw(Batch& batch); + void initTextureTransferHelper(); + static void transferGPUObject(const TexturePointer& texture); + + static std::shared_ptr _textureTransferHelper; + // Draw Stage void do_draw(Batch& batch, size_t paramOffset); void do_drawIndexed(Batch& batch, size_t paramOffset); @@ -484,6 +528,7 @@ protected: typedef void (GLBackend::*CommandCall)(Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; + }; }; diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 37a10e670b..4f714fb53d 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -83,7 +83,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe for (auto& b : framebuffer.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = GLBackend::syncGPUObject(*surface); + gltexture = GLBackend::syncGPUObject(surface); } else { gltexture = nullptr; } @@ -123,7 +123,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe if (framebuffer.getDepthStamp() != object->_depthStamp) { auto surface = framebuffer.getDepthStencilBuffer(); if (framebuffer.hasDepthStencil() && surface) { - gltexture = GLBackend::syncGPUObject(*surface); + gltexture = GLBackend::syncGPUObject(surface); } if (gltexture) { diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 046f1ff0e5..10845d1129 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -255,7 +255,7 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) { _stats._RSNumTextureBounded++; // Always make sure the GLObject is in sync - GLTexture* object = GLBackend::syncGPUObject(*resourceTexture); + GLTexture* object = GLBackend::syncGPUObject(resourceTexture); if (object) { GLuint to = object->_texture; GLuint target = object->_target; diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 242b9100e1..92123a68be 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -9,19 +9,81 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GPULogging.h" + +#include + #include "GLBackendShared.h" #include "GLTexelFormat.h" +#include "GLBackendTextureTransfer.h" using namespace gpu; -GLBackend::GLTexture::GLTexture() : - _storageStamp(0), - _contentStamp(0), - _texture(0), - _target(GL_TEXTURE_2D), - _size(0) +GLenum gpuToGLTextureType(const Texture& texture) { + // If we get here, we need to allocate and or update the content of the texture + // or it's already being transferred + switch (texture.getType()) { + case Texture::TEX_2D: + return GL_TEXTURE_2D; + break; + + case Texture::TEX_CUBE: + return GL_TEXTURE_CUBE_MAP; + break; + + default: + qFatal("Unsupported texture type"); + } + Q_UNREACHABLE(); + return GL_TEXTURE_2D; +} + +GLuint allocateSingleTexture() { + GLuint result; + glGenTextures(1, &result); + return result; +} + +const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +// Create the texture and allocate storage +GLBackend::GLTexture::GLTexture(const Texture& texture) : + _storageStamp(texture.getStamp()), _texture(allocateSingleTexture()), + _target(gpuToGLTextureType(texture)), _size((GLuint)texture.getSize()), _gpuTexture(texture) { Backend::incrementTextureGPUCount(); + Backend::updateTextureGPUMemoryUsage(0, _size); + Backend::setGPUObject(texture, this); + + GLsizei width = texture.getWidth(); + GLsizei height = texture.getHeight(); + GLsizei levels = 1; + if (texture.maxMip() > 0) { + if (texture.isAutogenerateMips()) { + while ((width | height) >> levels) { + ++levels; + } + } + levels = std::max(1, std::min(texture.maxMip() + 1, levels)); + } + + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat()); + withPreservedTexture(_target, [&] { + glBindTexture(_target, _texture); + (void)CHECK_GL_ERROR(); + // GO through the process of allocating the correct storage + if (GLEW_VERSION_4_2) { + glTexStorage2D(_target, levels, texelFormat.internalFormat, width, height); + } else { + glTexImage2D(_target, 0, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, 0); + } + (void)CHECK_GL_ERROR(); + syncSampler(texture.getSampler(), texture.getType(), this); + (void)CHECK_GL_ERROR(); + }); } GLBackend::GLTexture::~GLTexture() { @@ -32,199 +94,164 @@ GLBackend::GLTexture::~GLTexture() { Backend::decrementTextureGPUCount(); } -void GLBackend::GLTexture::setSize(GLuint size) { - Backend::updateTextureGPUMemoryUsage(_size, size); - _size = size; +bool GLBackend::GLTexture::invalid() const { + return _storageStamp < _gpuTexture.getStamp(); } -GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { - GLTexture* object = Backend::getGPUObject(texture); +bool GLBackend::GLTexture::outdated() const { + return _contentStamp < _gpuTexture.getDataStamp(); +} - // If GPU object already created and in sync - bool needUpdate = false; - if (object && (object->_storageStamp == texture.getStamp())) { - // If gpu object info is in sync with sysmem version - if (object->_contentStamp >= texture.getDataStamp()) { - // Then all good, GPU object is ready to be used - return object; - } else { - // Need to update the content of the GPU object from the source sysmem of the texture - needUpdate = true; - } - } else if (!texture.isDefined()) { +bool GLBackend::GLTexture::ready() const { + // If we have an invalid texture, we're never ready + if (invalid()) { + return false; + } + + // If we're out of date, but the transfer is in progress, report ready + // as a special case + auto syncState = _syncState.load(); + + if (outdated()) { + return Pending == syncState; + } + + return Idle == syncState; +} + +//#define USE_PBO + +// Move content bits from the CPU to the GPU for a given mip / face +void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat()); +#ifdef USE_PBO + GLuint pixelBufferID; + glGenBuffers(1, &pixelBufferID); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBufferID); + //if (GLEW_VERSION_4_4) { + // glBufferStorage(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW); + //} else { + glBufferData(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW); + //} + void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + memcpy(mappedBuffer, mip->readData(), mip->getSize()); + //// use while PBO is still bound, assumes GL_TEXTURE_2D and offset 0 + glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, 0); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glDeleteBuffers(1, &pixelBufferID); +#else + //glTexImage2D(target, 0, internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.format, texelFormat.type, bytes); + glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); + (void)CHECK_GL_ERROR(); +#endif +} + +// Move content bits from the CPU to the GPU +void GLBackend::GLTexture::transfer() const { + PROFILE_RANGE(__FUNCTION__); + qDebug() << "Transferring texture: " << _texture; + // Need to update the content of the GPU object from the source sysmem of the texture + if (_contentStamp >= _gpuTexture.getDataStamp()) { + return; + } + + glBindTexture(_target, _texture); + // GO through the process of allocating the correct storage and/or update the content + switch (_gpuTexture.getType()) { + case Texture::TEX_2D: + if (_gpuTexture.isStoredMipFaceAvailable(0)) { + transferMip(GL_TEXTURE_2D, _gpuTexture.accessStoredMipFace(0)); + } + break; + + case Texture::TEX_CUBE: + // transfer pixels from each faces + for (int f = 0; f < CUBE_NUM_FACES; f++) { + if (_gpuTexture.isStoredMipFaceAvailable(0, f)) { + transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f)); + } + } + break; + + default: + qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported"; + break; + } + + if (_gpuTexture.isAutogenerateMips()) { + glGenerateMipmap(_target); + (void)CHECK_GL_ERROR(); + } +} + +// Do any post-transfer operations that might be required on the main context / rendering thread +void GLBackend::GLTexture::postTransfer() { + setSyncState(GLTexture::Idle); + switch (_gpuTexture.getType()) { + case Texture::TEX_2D: + // At this point the mip piels have been loaded, we can notify + _gpuTexture.notifyMipFaceGPULoaded(0, 0); + break; + + case Texture::TEX_CUBE: + for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) { + // At this point the mip piels have been loaded, we can notify + _gpuTexture.notifyMipFaceGPULoaded(0, f); + } + break; + + default: + qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported"; + break; + } +} + +GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { + const Texture& texture = *texturePointer; + if (!texture.isDefined()) { // NO texture definition yet so let's avoid thinking return nullptr; } + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GLTexture* object = Backend::getGPUObject(texture); + if (object && object->ready()) { + return object; + } + + // Object isn't ready, check what we need to do... + + // Create the texture if need be (force re-creation if the storage stamp changes + // for easier use of immutable storage) + if (!object || object->invalid()) { + // This automatically destroys the old texture + object = new GLTexture(texture); + } + // need to have a gpu object? - if (!object) { - object = new GLTexture(); - glGenTextures(1, &object->_texture); - (void) CHECK_GL_ERROR(); - Backend::setGPUObject(texture, object); + if (texture.getNumSlices() != 1) { + return object; } - // GO through the process of allocating the correct storage and/or update the content - switch (texture.getType()) { - case Texture::TEX_2D: { - if (texture.getNumSlices() == 1) { - GLint boundTex = -1; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - glBindTexture(GL_TEXTURE_2D, object->_texture); - - if (needUpdate) { - if (texture.isStoredMipFaceAvailable(0)) { - Texture::PixelsPointer mip = texture.accessStoredMipFace(0); - const GLvoid* bytes = mip->readData(); - Element srcFormat = mip->getFormat(); - - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glBindTexture(GL_TEXTURE_2D, object->_texture); - glTexSubImage2D(GL_TEXTURE_2D, 0, - texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, - texelFormat.format, texelFormat.type, bytes); - - if (texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - - object->_target = GL_TEXTURE_2D; - - syncSampler(texture.getSampler(), texture.getType(), object); - - - // At this point the mip piels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, 0); - - object->_contentStamp = texture.getDataStamp(); - } - } else { - const GLvoid* bytes = 0; - Element srcFormat = texture.getTexelFormat(); - if (texture.isStoredMipFaceAvailable(0)) { - Texture::PixelsPointer mip = texture.accessStoredMipFace(0); - - bytes = mip->readData(); - srcFormat = mip->getFormat(); - - object->_contentStamp = texture.getDataStamp(); - } - - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glTexImage2D(GL_TEXTURE_2D, 0, - texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, - texelFormat.format, texelFormat.type, bytes); - - if (bytes && texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - object->_target = GL_TEXTURE_2D; - - syncSampler(texture.getSampler(), texture.getType(), object); - - // At this point the mip pixels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, 0); - - object->_storageStamp = texture.getStamp(); - object->_contentStamp = texture.getDataStamp(); - object->setSize((GLuint)texture.getSize()); - } - - glBindTexture(GL_TEXTURE_2D, boundTex); - } - break; + // Object might be outdated, if so, start the transfer + // (outdated objects that are already in transfer will have reported 'true' for ready() + if (object->outdated()) { + _textureTransferHelper->transferTexture(texturePointer); } - case Texture::TEX_CUBE: { - if (texture.getNumSlices() == 1) { - GLint boundTex = -1; - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture); - const int NUM_FACES = 6; - const GLenum FACE_LAYOUT[] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; - if (needUpdate) { - glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture); - // transfer pixels from each faces - for (int f = 0; f < NUM_FACES; f++) { - if (texture.isStoredMipFaceAvailable(0, f)) { - Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f); - Element srcFormat = mipFace->getFormat(); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0, - texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData())); - - // At this point the mip pixels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, f); - } - } - - if (texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - - object->_target = GL_TEXTURE_CUBE_MAP; - - syncSampler(texture.getSampler(), texture.getType(), object); - - object->_contentStamp = texture.getDataStamp(); - - } else { - glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture); - - // transfer pixels from each faces - for (int f = 0; f < NUM_FACES; f++) { - if (texture.isStoredMipFaceAvailable(0, f)) { - Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f); - Element srcFormat = mipFace->getFormat(); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0, - texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData())); - - // At this point the mip pixels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, f); - } - } - - if (texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } else { - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - - object->_target = GL_TEXTURE_CUBE_MAP; - - syncSampler(texture.getSampler(), texture.getType(), object); - - object->_storageStamp = texture.getStamp(); - object->_contentStamp = texture.getDataStamp(); - object->setSize((GLuint)texture.getSize()); - } - - glBindTexture(GL_TEXTURE_CUBE_MAP, boundTex); - } - break; + if (GLTexture::Transferred == object->getSyncState()) { + object->postTransfer(); } - default: - qCDebug(gpulogging) << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported"; - } - (void) CHECK_GL_ERROR(); return object; } +std::shared_ptr GLBackend::_textureTransferHelper; +void GLBackend::initTextureTransferHelper() { + _textureTransferHelper = std::make_shared(); +} GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { if (!texture) { @@ -232,7 +259,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { } GLTexture* object { nullptr }; if (sync) { - object = GLBackend::syncGPUObject(*texture); + object = GLBackend::syncGPUObject(texture); } else { object = Backend::getGPUObject(*texture); } @@ -243,38 +270,37 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { } } -void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object) { +void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object) { if (!object) return; - if (!object->_texture) return; class GLFilterMode { public: GLint minFilter; GLint magFilter; }; - static const GLFilterMode filterModes[] = { - {GL_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_POINT, - {GL_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR, - {GL_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT, - {GL_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR, - - {GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT, - {GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT, - {GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_MAG_POINT_MIP_LINEAR, - {GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, - {GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_POINT_MAG_MIP_LINEAR, - {GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_MIP_POINT, - {GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, - {GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR_MIP_POINT, - {GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_MIP_LINEAR, - {GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} //FILTER_ANISOTROPIC, + static const GLFilterMode filterModes[] = { + { GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT, + { GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR, + { GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT, + { GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR, + + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR, + { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC, }; auto fm = filterModes[sampler.getFilter()]; glTexParameteri(object->_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTexParameteri(object->_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); - static const GLenum comparisonFuncs[] = { + static const GLenum comparisonFuncs[] = { GL_NEVER, GL_LESS, GL_EQUAL, @@ -291,7 +317,7 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur glTexParameteri(object->_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); } - static const GLenum wrapModes[] = { + static const GLenum wrapModes[] = { GL_REPEAT, // WRAP_REPEAT, GL_MIRRORED_REPEAT, // WRAP_MIRROR, GL_CLAMP_TO_EDGE, // WRAP_CLAMP, @@ -302,23 +328,20 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur glTexParameteri(object->_target, GL_TEXTURE_WRAP_T, wrapModes[sampler.getWrapModeV()]); glTexParameteri(object->_target, GL_TEXTURE_WRAP_R, wrapModes[sampler.getWrapModeW()]); - glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*) &sampler.getBorderColor()); + glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); glTexParameteri(object->_target, GL_TEXTURE_BASE_LEVEL, sampler.getMipOffset()); - glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float) sampler.getMinMip()); + glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(object->_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - } - - void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); if (!resourceTexture) { return; } - GLTexture* object = GLBackend::syncGPUObject(*resourceTexture); + GLTexture* object = GLBackend::syncGPUObject(resourceTexture); if (!object) { return; } @@ -333,7 +356,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { if (freeSlot < 0) { // If had to use slot 0 then restore state - GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]); + GLTexture* boundObject = GLBackend::syncGPUObject(_resource._textures[0]); if (boundObject) { glBindTexture(boundObject->_target, boundObject->_texture); } diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp new file mode 100644 index 0000000000..aa5839b5b2 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp @@ -0,0 +1,109 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 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 "GLBackendTextureTransfer.h" + +#include "GLBackendShared.h" +#include "GLTexelFormat.h" + +#ifdef THREADED_TEXTURE_TRANSFER +#include +#include +#include +#endif + +using namespace gpu; + +static ProgramPtr _program; +static ProgramPtr _cubeProgram; +static ShapeWrapperPtr _plane; +static ShapeWrapperPtr _skybox; +static BasicFramebufferWrapperPtr _framebuffer; + +GLTextureTransferHelper::GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + _canvas = std::make_shared(); + _canvas->create(QOpenGLContextWrapper::currentContext()); + if (!_canvas->makeCurrent()) { + qFatal("Unable to create texture transfer context"); + } + _canvas->doneCurrent(); + initialize(true, QThread::LowPriority); + _canvas->moveToThreadWithContext(_thread); +#endif +} + +void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { + GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); +#ifdef THREADED_TEXTURE_TRANSFER + TextureTransferPackage package { texturePointer, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) }; + glFlush(); + object->setSyncState(GLBackend::GLTexture::Pending); + queueItem(package); +#else + object->transfer(); + object->postTransfer(); +#endif +} + +void GLTextureTransferHelper::setup() { +#ifdef THREADED_TEXTURE_TRANSFER + _canvas->makeCurrent(); + _program = loadDefaultShader(); + _plane = loadPlane(_program); + _cubeProgram = loadCubemapShader(); + _skybox = loadSkybox(_cubeProgram); + _framebuffer = std::make_shared(); + _framebuffer->Init({ 100, 100 }); + _framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw); +#endif +} + +bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { + for (auto package : messages) { + glWaitSync(package.fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(package.fence); + TexturePointer texturePointer = package.texture.lock(); + // Texture no longer exists, move on to the next + if (!texturePointer) { + continue; + } + + GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); + object->transfer(); + + // Now force a draw using the texture + try { + switch (texturePointer->getType()) { + case Texture::TEX_2D: + _program->Use(); + _plane->Use(); + _plane->Draw(); + break; + + case Texture::TEX_CUBE: + _cubeProgram->Use(); + _skybox->Use(); + _skybox->Draw(); + break; + + default: + qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << texturePointer->getType() << " not supported"; + break; + } + } catch (const std::runtime_error& error) { + qWarning() << "Failed to render texture on background thread: " << error.what(); + } + + glBindTexture(object->_target, 0); + glFinish(); + object->_contentStamp = texturePointer->getDataStamp(); + object->setSyncState(GLBackend::GLTexture::Transferred); + } + return true; +} diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.h b/libraries/gpu/src/gpu/GLBackendTextureTransfer.h new file mode 100644 index 0000000000..3a147defdf --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.h @@ -0,0 +1,61 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 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 "GLBackendShared.h" + +#define THREADED_TEXTURE_TRANSFER + +class OffscreenGLCanvas; + +namespace gpu { + +struct TextureTransferPackage { + std::weak_ptr texture; + GLsync fence; +}; + +class GLTextureTransferHelper : public GenericQueueThread { +public: + GLTextureTransferHelper(); + void transferTexture(const gpu::TexturePointer& texturePointer); + void postTransfer(const gpu::TexturePointer& texturePointer); + +protected: + void setup() override; + bool processQueueItems(const Queue& messages) override; + void transferTextureSynchronous(const gpu::Texture& texture); + +private: + std::shared_ptr _canvas; +}; + +template +void withPreservedTexture(GLenum target, F f) { + GLint boundTex = -1; + switch (target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + f(); + + glBindTexture(target, boundTex); + (void)CHECK_GL_ERROR(); +} + +} diff --git a/libraries/gpu/src/gpu/GLTexelFormat.h b/libraries/gpu/src/gpu/GLTexelFormat.h index 274b5eeba3..189189f68b 100644 --- a/libraries/gpu/src/gpu/GLTexelFormat.h +++ b/libraries/gpu/src/gpu/GLTexelFormat.h @@ -14,6 +14,9 @@ public: GLenum format; GLenum type; + static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) { + return evalGLTexelFormat(dstFormat, dstFormat); + } static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) { using namespace gpu; if (dstFormat != srcFormat) { diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index c1c31ab50a..00a80a2864 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -46,6 +46,8 @@ void GenericThread::initialize(bool isThreaded, QThread::Priority priority) { _thread->start(); _thread->setPriority(priority); + } else { + setup(); } } @@ -60,10 +62,16 @@ void GenericThread::terminate() { _thread->deleteLater(); _thread = NULL; } + } else { + shutdown(); } } void GenericThread::threadRoutine() { + if (_isThreaded) { + setup(); + } + while (!_stopThread) { // override this function to do whatever your class actually does, return false to exit thread early @@ -78,8 +86,13 @@ void GenericThread::threadRoutine() { } } - // If we were on a thread, then quit our thread - if (_isThreaded && _thread) { - _thread->quit(); + if (_isThreaded) { + shutdown(); + + // If we were on a thread, then quit our thread + if (_thread) { + _thread->quit(); + } } + } diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 8362d3ba57..47f6d9dacd 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -33,9 +33,6 @@ public: /// Call to stop the thread void terminate(); - /// Override this function to do whatever your class actually does, return false to exit thread early. - virtual bool process() = 0; - virtual void terminating() { }; // lets your subclass know we're terminating, and it should respond appropriately bool isThreaded() const { return _isThreaded; } @@ -48,6 +45,10 @@ signals: void finished(); protected: + /// Override this function to do whatever your class actually does, return false to exit thread early. + virtual bool process() = 0; + virtual void setup() {}; + virtual void shutdown() {}; /// Locks all the resources of the thread. void lock() { _mutex.lock(); } From e70319d83dce7a0ed7b93018b7f54cdd56908630 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 4 Apr 2016 10:53:16 -0700 Subject: [PATCH 12/23] don't re-connect multiple times to QNetworkAccessManager signal --- ice-server/src/IceServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index c1e92f276f..b66ccaa057 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -53,6 +53,10 @@ IceServer::IceServer(int argc, char* argv[]) : QTimer* inactivePeerTimer = new QTimer(this); connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers); inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS); + + // handle public keys when they arrive from the QNetworkAccessManager + auto& networkAccessManager = NetworkAccessManager::getInstance(); + connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished); } bool IceServer::packetVersionMatch(const udt::Packet& packet) { @@ -200,7 +204,6 @@ bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& pla void IceServer::requestDomainPublicKey(const QUuid& domainID) { // send a request to the metaverse API for the public key for this domain auto& networkAccessManager = NetworkAccessManager::getInstance(); - connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished); QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL }; QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID)); From d23d0f64326efc4ebb6cb41afa69ebafd4e54ffa Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 4 Apr 2016 10:57:04 -0700 Subject: [PATCH 13/23] Restore thread priority after tex parsing --- .../src/model-networking/TextureCache.cpp | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index ff189b46f7..bbe907694a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -11,20 +11,22 @@ #include "TextureCache.h" +#include +#include +#include +#include +#include + #include #include #include -#include -#include -#include -#include -#include -#include - #include +#include +#include + #include "ModelNetworkingLogging.h" TextureCache::TextureCache() { @@ -283,10 +285,12 @@ void ImageReader::run() { originalPriority = QThread::NormalPriority; } QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority]{ + QThread::currentThread()->setPriority(originalPriority); + }); if (!_resource.data()) { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - QThread::currentThread()->setPriority(originalPriority); return; } @@ -318,7 +322,6 @@ void ImageReader::run() { auto resource = _resource.toStrongRef(); if (!resource) { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - QThread::currentThread()->setPriority(originalPriority); return; } @@ -336,8 +339,6 @@ void ImageReader::run() { Q_ARG(void*, texture), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); } - - QThread::currentThread()->setPriority(originalPriority); } void NetworkTexture::setImage(void* voidTexture, int originalWidth, From a53cb2e532e568caed6f6e1b40d8cccecf2a4fa1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 4 Apr 2016 11:10:50 -0700 Subject: [PATCH 14/23] use _gravity rather than _acceleration also tweaks and comments about supporting low gravity --- libraries/entities/src/EntityItem.cpp | 74 +++++++++++++++++++-------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5b9791ca7b..578d57273f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -895,9 +895,9 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); } - const float EPSILON_ANGULAR_VELOCITY_LENGTH_SQUARED = 0.0017453f * 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec - if (glm::length2(angularVelocity) < EPSILON_ANGULAR_VELOCITY_LENGTH_SQUARED) { - angularVelocity = ENTITY_ITEM_ZERO_VEC3; + const float MIN_KINEMATIC_ANGULAR_SPEED_SQUARED = 0.0017453f * 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec + if (glm::length2(angularVelocity) < MIN_KINEMATIC_ANGULAR_SPEED_SQUARED) { + angularVelocity = Vectors::ZERO; } else { // for improved agreement with the way Bullet integrates rotations we use an approximation // and break the integration into bullet-sized substeps @@ -912,43 +912,75 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { isMoving = true; } } - if (glm::length2(linearVelocity) > 0.0f) { + glm::vec3 position = transform.getTranslation(); + + const float MIN_KINEMATIC_LINEAR_SPEED_SQUARED = 1.0e-6f; // 1mm/sec ^2 + bool hasLinearVelocity = (glm::length2(linearVelocity) > MIN_KINEMATIC_LINEAR_SPEED_SQUARED ); + if (hasLinearVelocity) { // linear damping if (_damping > 0.0f) { linearVelocity *= powf(1.0f - _damping, timeElapsed); } - glm::vec3 linearAcceleration = _acceleration; - bool nonZeroAcceleration = (glm::length2(_acceleration) > 0.0f); - if (nonZeroAcceleration) { - // acceleration is in world-frame but we need it in local-frame - bool success; - Transform parentTransform = getParentTransform(success); - if (success) { - linearAcceleration = glm::inverse(parentTransform.getRotation()) * linearAcceleration; - } + // integrate position forward sans acceleration + position += linearVelocity * timeElapsed; + } + + const float MIN_KINEMATIC_GRAVITY_MOTION_SQUARED = 1.0e-6f; // 0.001 mm/sec^2 + bool hasGravity = (glm::length2(_gravity) > MIN_KINEMATIC_GRAVITY_MOTION_SQUARED); + if (hasGravity) { + // acceleration is in world-frame but we need it in local-frame + glm::vec3 linearAcceleration = _gravity; + bool success; + Transform parentTransform = getParentTransform(success); + if (success) { + linearAcceleration = glm::inverse(parentTransform.getRotation()) * linearAcceleration; } - // integrate position forward - glm::vec3 position = transform.getTranslation() + (linearVelocity * timeElapsed) + 0.5f * linearAcceleration * timeElapsed * timeElapsed; - transform.setTranslation(position); + // integrate position's acceleration term + position += 0.5f * linearAcceleration * timeElapsed * timeElapsed; // integrate linearVelocity linearVelocity += linearAcceleration * timeElapsed; + } - const float EPSILON_LINEAR_VELOCITY_LENGTH_SQUARED = 1.0e-6f; // 1mm/sec ^2 - if (glm::length2(linearVelocity) < EPSILON_LINEAR_VELOCITY_LENGTH_SQUARED) { - setVelocity(ENTITY_ITEM_ZERO_VEC3); - if (nonZeroAcceleration) { + if (hasLinearVelocity || hasGravity) { + // We MUST eventually stop kinematic motion for slow entities otherwise they will take + // a looooong time to settle down, so we remeasure linear speed and zero the velocity + // if it is too small. + if (glm::length2(linearVelocity) < MIN_KINEMATIC_LINEAR_SPEED_SQUARED) { + linearVelocity = Vectors::ZERO; + if (!hasLinearVelocity) { + // Despite some gravity the final linear velocity is still too small to defeat the + // "effective resistance of free-space" which means we must reset position back to + // where it started since otherwise the entity may creep vveerrryy sslloowwllyy at + // a constant speed. + position = transform.getTranslation(); + + // Ultimately what this means is that there is some minimum gravity we can + // fully support for kinematic motion. It's exact value is a function of this + // entity's linearDamping and the hardcoded MIN_KINEMATIC_FOO parameters above, + // but the theoretical minimum gravity for zero damping at 90Hz is: + // + // minGravity = minSpeed / dt = 0.001 m/sec * 90 /sec = 0.09 m/sec^2 + // + // In practice the true minimum is half that value, since if the frame rate is ever + // less than the expected then sometimes dt will be twice as long. + // + // Since we don't set isMoving true here this entity is destined to transition to + // STATIC unless it has some angular motion keeping it alive. + } else { isMoving = true; } } else { isMoving = true; } } + transform.setTranslation(position); setLocalTransformAndVelocities(transform, linearVelocity, angularVelocity); + if (!isMoving) { - // flag this entity to be removed from kinematic motion + // flag this entity to transition from KINEMATIC to STATIC _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } } From a08ab8e127d066aacf73ca12cac9679f1a8b19f1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 4 Apr 2016 11:48:45 -0700 Subject: [PATCH 15/23] restore setFlag in simulateKinematicMotion() --- libraries/entities/src/EntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 578d57273f..9fa58c9187 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -979,7 +979,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { transform.setTranslation(position); setLocalTransformAndVelocities(transform, linearVelocity, angularVelocity); - if (!isMoving) { + if (setFlags && !isMoving) { // flag this entity to transition from KINEMATIC to STATIC _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } From aa0245b9c24822410a8e32cac698a6828d9ff1e7 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 4 Apr 2016 12:20:45 -0700 Subject: [PATCH 16/23] Debug when limit of checkins reached --- libraries/networking/src/NodeList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 02bb17e870..63189abb3e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -336,6 +336,7 @@ void NodeList::sendDomainServerCheckIn() { if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS // so emit our signal that says that + qDebug() << "Limit of silent domain checkins reached"; emit limitOfSilentDomainCheckInsReached(); } From a9197385b5437f160e656867e8b6fea66d40e9e5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 4 Apr 2016 12:24:26 -0700 Subject: [PATCH 17/23] Make debug builds work again. Only run mt.exe on the manifest in non development builds. --- interface/CMakeLists.txt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index b7aeb7696e..2e50502840 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -107,12 +107,15 @@ elseif(WIN32) # add an executable that also has the icon itself and the configured rc file as resources add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT}) - add_custom_command( - TARGET ${TARGET_NAME} - POST_BUILD - COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$"\;\#1 -outputresource:"$"\;\#1 - COMMENT "Adding OS version support manifest to exe" - ) + if ( NOT DEV_BUILD ) + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$"\;\#1 -outputresource:"$"\;\#1 + COMMENT "Adding OS version support manifest to exe" + ) + endif() + else() add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif() From d7b13d226f5b469098c7556ebef75199165d0d7d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 4 Apr 2016 12:36:18 -0700 Subject: [PATCH 18/23] Application: fix for jittery hands when moving. The issue was that the hands are sampled in sensor coordinates. But when the avatar is moved via physics, the sensor to world matrix must be updated to move the hands in the world by the correct amount. These new hand positions are then computed before IK is performed. --- interface/src/Application.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0172b3ce3a..9e97fba466 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1470,10 +1470,6 @@ void Application::paintGL() { // update the avatar with a fresh HMD pose getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); - // update sensorToWorldMatrix for camera and hand controllers - getMyAvatar()->updateSensorToWorldMatrix(); - - auto lodManager = DependencyManager::get(); @@ -3406,6 +3402,9 @@ void Application::update(float deltaTime) { avatarManager->updateOtherAvatars(deltaTime); } + // update sensorToWorldMatrix for camera and hand controllers + getMyAvatar()->updateSensorToWorldMatrix(); + qApp->updateMyAvatarLookAtPosition(); { From ace4546dec1a5fcbb064baefbd52d266febba836 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 4 Apr 2016 12:49:03 -0700 Subject: [PATCH 19/23] make handGrabController search ray follow controller rather than IK driven hand --- examples/controllers/handControllerGrab.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index a3bed0a256..d8795341bb 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -495,7 +495,8 @@ function MyController(hand) { } }; - this.searchIndicatorOn = function(handPosition, distantPickRay) { + this.searchIndicatorOn = function(distantPickRay) { + var handPosition = distantPickRay.origin; var SEARCH_SPHERE_SIZE = 0.011; var SEARCH_SPHERE_FOLLOW_RATE = 0.50; @@ -857,7 +858,9 @@ function MyController(hand) { var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; - var currentControllerPosition = Controller.getPoseValue(controllerHandInput).position; + var currentControllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, + Controller.getPoseValue(controllerHandInput).translation), + MyAvatar.position); var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? @@ -865,19 +868,13 @@ function MyController(hand) { var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); var distantPickRay = { - origin: PICK_WITH_HAND_RAY ? handPosition : Camera.position, + origin: PICK_WITH_HAND_RAY ? currentControllerPosition : Camera.position, direction: PICK_WITH_HAND_RAY ? Quat.getUp(controllerRotation) : Vec3.mix(Quat.getUp(controllerRotation), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE }; - var searchVisualizationPickRay = { - origin: currentControllerPosition, - direction: Quat.getUp(this.getHandRotation()), - length: PICK_MAX_DISTANCE - }; - // Pick at some maximum rate, not always var pickRays = []; var now = Date.now(); @@ -1086,7 +1083,7 @@ function MyController(hand) { this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); } - this.searchIndicatorOn(handPosition, distantPickRay); + this.searchIndicatorOn(distantPickRay); Reticle.setVisible(false); }; @@ -1668,7 +1665,7 @@ function MyController(hand) { if (intersection.intersects) { this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); } - this.searchIndicatorOn(handPosition, pickRay); + this.searchIndicatorOn(pickRay); } } From b3b5c5d1eacee6d761034477d16b5c256f5d711b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 4 Apr 2016 13:14:52 -0700 Subject: [PATCH 20/23] small change to trigger a build --- interface/src/Application.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 61d0f4f995..fdd31ed579 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -169,9 +169,7 @@ #include "Util.h" #include "InterfaceParentFinder.h" - - -// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU +// ON Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) extern "C" { From 69bb0ebd741f56f388e560728359d759b8d9d5ff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 4 Apr 2016 16:11:54 -0700 Subject: [PATCH 21/23] Revert "faster kinematic motion for entities" --- libraries/entities/src/EntityItem.cpp | 161 ++++++++++-------- libraries/physics/src/EntityMotionState.cpp | 19 ++- libraries/physics/src/EntityMotionState.h | 6 +- libraries/physics/src/ObjectMotionState.cpp | 21 +-- libraries/physics/src/ObjectMotionState.h | 10 +- libraries/physics/src/PhysicsEngine.cpp | 31 +--- libraries/physics/src/PhysicsEngine.h | 6 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 26 +-- .../physics/src/ThreadSafeDynamicsWorld.h | 1 - libraries/shared/src/GLMHelpers.cpp | 8 + libraries/shared/src/GLMHelpers.h | 4 +- libraries/shared/src/PhysicsCollisionGroups.h | 4 +- libraries/shared/src/SharedUtil.cpp | 17 +- libraries/shared/src/SharedUtil.h | 6 +- libraries/shared/src/SpatiallyNestable.cpp | 50 ++---- libraries/shared/src/SpatiallyNestable.h | 7 - libraries/shared/src/Transform.cpp | 4 + libraries/shared/src/Transform.h | 2 +- 18 files changed, 164 insertions(+), 219 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9fa58c9187..431d638063 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -876,112 +876,123 @@ void EntityItem::simulate(const quint64& now) { } void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { - if (hasActions() || timeElapsed < 0.0f) { +#ifdef WANT_DEBUG + qCDebug(entities) << "EntityItem::simulateKinematicMotion timeElapsed" << timeElapsed; +#endif + + const float MIN_TIME_SKIP = 0.0f; + const float MAX_TIME_SKIP = 1.0f; // in seconds + + timeElapsed = glm::clamp(timeElapsed, MIN_TIME_SKIP, MAX_TIME_SKIP); + + if (hasActions()) { return; } - const float MAX_TIME_ELAPSED = 1.0f; // seconds - timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED); + if (hasLocalAngularVelocity()) { + glm::vec3 localAngularVelocity = getLocalAngularVelocity(); - Transform transform; - glm::vec3 linearVelocity; - glm::vec3 angularVelocity; - getLocalTransformAndVelocities(transform, linearVelocity, angularVelocity); - - bool isMoving = false; - if (glm::length2(angularVelocity) > 0.0f) { // angular damping if (_angularDamping > 0.0f) { - angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); + localAngularVelocity *= powf(1.0f - _angularDamping, timeElapsed); + #ifdef WANT_DEBUG + qCDebug(entities) << " angularDamping :" << _angularDamping; + qCDebug(entities) << " newAngularVelocity:" << localAngularVelocity; + #endif } - const float MIN_KINEMATIC_ANGULAR_SPEED_SQUARED = 0.0017453f * 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec - if (glm::length2(angularVelocity) < MIN_KINEMATIC_ANGULAR_SPEED_SQUARED) { - angularVelocity = Vectors::ZERO; + float angularSpeed = glm::length(localAngularVelocity); + + const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec + if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (setFlags && angularSpeed > 0.0f) { + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; + } + localAngularVelocity = ENTITY_ITEM_ZERO_VEC3; } else { // for improved agreement with the way Bullet integrates rotations we use an approximation // and break the integration into bullet-sized substeps - glm::quat rotation = transform.getRotation(); + glm::quat rotation = getRotation(); float dt = timeElapsed; - while (dt > 0.0f) { - glm::quat dQ = computeBulletRotationStep(angularVelocity, glm::min(dt, PHYSICS_ENGINE_FIXED_SUBSTEP)); + while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) { + glm::quat dQ = computeBulletRotationStep(localAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP); rotation = glm::normalize(dQ * rotation); dt -= PHYSICS_ENGINE_FIXED_SUBSTEP; } - transform.setRotation(rotation); - isMoving = true; - } - } - glm::vec3 position = transform.getTranslation(); + // NOTE: this final partial substep can drift away from a real Bullet simulation however + // it only becomes significant for rapidly rotating objects + // (e.g. around PI/4 radians per substep, or 7.5 rotations/sec at 60 substeps/sec). + glm::quat dQ = computeBulletRotationStep(localAngularVelocity, dt); + rotation = glm::normalize(dQ * rotation); - const float MIN_KINEMATIC_LINEAR_SPEED_SQUARED = 1.0e-6f; // 1mm/sec ^2 - bool hasLinearVelocity = (glm::length2(linearVelocity) > MIN_KINEMATIC_LINEAR_SPEED_SQUARED ); - if (hasLinearVelocity) { - // linear damping - if (_damping > 0.0f) { - linearVelocity *= powf(1.0f - _damping, timeElapsed); + setRotation(rotation); } - // integrate position forward sans acceleration - position += linearVelocity * timeElapsed; + setLocalAngularVelocity(localAngularVelocity); } - const float MIN_KINEMATIC_GRAVITY_MOTION_SQUARED = 1.0e-6f; // 0.001 mm/sec^2 - bool hasGravity = (glm::length2(_gravity) > MIN_KINEMATIC_GRAVITY_MOTION_SQUARED); - if (hasGravity) { - // acceleration is in world-frame but we need it in local-frame - glm::vec3 linearAcceleration = _gravity; + if (hasLocalVelocity()) { + + // acceleration is in the global frame, so transform it into the local frame. + // TODO: Move this into SpatiallyNestable. bool success; - Transform parentTransform = getParentTransform(success); + Transform transform = getParentTransform(success); + glm::vec3 localAcceleration(glm::vec3::_null); if (success) { - linearAcceleration = glm::inverse(parentTransform.getRotation()) * linearAcceleration; + localAcceleration = glm::inverse(transform.getRotation()) * getAcceleration(); + } else { + localAcceleration = getAcceleration(); } - // integrate position's acceleration term - position += 0.5f * linearAcceleration * timeElapsed * timeElapsed; + // linear damping + glm::vec3 localVelocity = getLocalVelocity(); + if (_damping > 0.0f) { + localVelocity *= powf(1.0f - _damping, timeElapsed); + #ifdef WANT_DEBUG + qCDebug(entities) << " damping:" << _damping; + qCDebug(entities) << " velocity AFTER dampingResistance:" << localVelocity; + qCDebug(entities) << " glm::length(velocity):" << glm::length(localVelocity); + #endif + } - // integrate linearVelocity - linearVelocity += linearAcceleration * timeElapsed; - } + // integrate position forward + glm::vec3 localPosition = getLocalPosition(); + glm::vec3 newLocalPosition = localPosition + (localVelocity * timeElapsed) + 0.5f * localAcceleration * timeElapsed * timeElapsed; - if (hasLinearVelocity || hasGravity) { - // We MUST eventually stop kinematic motion for slow entities otherwise they will take - // a looooong time to settle down, so we remeasure linear speed and zero the velocity - // if it is too small. - if (glm::length2(linearVelocity) < MIN_KINEMATIC_LINEAR_SPEED_SQUARED) { - linearVelocity = Vectors::ZERO; - if (!hasLinearVelocity) { - // Despite some gravity the final linear velocity is still too small to defeat the - // "effective resistance of free-space" which means we must reset position back to - // where it started since otherwise the entity may creep vveerrryy sslloowwllyy at - // a constant speed. - position = transform.getTranslation(); + #ifdef WANT_DEBUG + qCDebug(entities) << " EntityItem::simulate()...."; + qCDebug(entities) << " timeElapsed:" << timeElapsed; + qCDebug(entities) << " old AACube:" << getMaximumAACube(); + qCDebug(entities) << " old position:" << localPosition; + qCDebug(entities) << " old velocity:" << localVelocity; + qCDebug(entities) << " old getAABox:" << getAABox(); + qCDebug(entities) << " newPosition:" << newPosition; + qCDebug(entities) << " glm::distance(newPosition, position):" << glm::distance(newLocalPosition, localPosition); + #endif - // Ultimately what this means is that there is some minimum gravity we can - // fully support for kinematic motion. It's exact value is a function of this - // entity's linearDamping and the hardcoded MIN_KINEMATIC_FOO parameters above, - // but the theoretical minimum gravity for zero damping at 90Hz is: - // - // minGravity = minSpeed / dt = 0.001 m/sec * 90 /sec = 0.09 m/sec^2 - // - // In practice the true minimum is half that value, since if the frame rate is ever - // less than the expected then sometimes dt will be twice as long. - // - // Since we don't set isMoving true here this entity is destined to transition to - // STATIC unless it has some angular motion keeping it alive. - } else { - isMoving = true; + localPosition = newLocalPosition; + + // apply effective acceleration, which will be the same as gravity if the Entity isn't at rest. + localVelocity += localAcceleration * timeElapsed; + + float speed = glm::length(localVelocity); + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { + setVelocity(ENTITY_ITEM_ZERO_VEC3); + if (setFlags && speed > 0.0f) { + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } } else { - isMoving = true; + setLocalPosition(localPosition); + setLocalVelocity(localVelocity); } - } - transform.setTranslation(position); - setLocalTransformAndVelocities(transform, linearVelocity, angularVelocity); - if (setFlags && !isMoving) { - // flag this entity to transition from KINEMATIC to STATIC - _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; + #ifdef WANT_DEBUG + qCDebug(entities) << " new position:" << position; + qCDebug(entities) << " new velocity:" << velocity; + qCDebug(entities) << " new AACube:" << getMaximumAACube(); + qCDebug(entities) << " old getAABox:" << getAABox(); + #endif } } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5f168d2e33..e3952ba1d6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -94,7 +94,7 @@ void EntityMotionState::updateServerPhysicsVariables() { } // virtual -void EntityMotionState::handleEasyChanges(uint32_t& flags) { +bool EntityMotionState::handleEasyChanges(uint32_t& flags) { assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); @@ -137,6 +137,8 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { _body->activate(); } + + return true; } @@ -173,13 +175,11 @@ bool EntityMotionState::isMoving() const { // (2) at the beginning of each simulation step for KINEMATIC RigidBody's -- // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { - BT_PROFILE("getWorldTransform"); if (!_entity) { return; } assert(entityTreeIsLocked()); if (_motionType == MOTION_TYPE_KINEMATIC) { - BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation. uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); @@ -420,18 +420,19 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; if (accVsGravity < ACCELERATION_EQUIVALENT_EPSILON_RATIO * gravityLength) { // acceleration measured during the most recent simulation step was close to gravity. - if (_accelerationNearlyGravityCount < STEPS_TO_DECIDE_BALLISTIC) { - // only increment this if we haven't reached the threshold yet, to avoid overflowing the counter - ++_accelerationNearlyGravityCount; + if (getAccelerationNearlyGravityCount() < STEPS_TO_DECIDE_BALLISTIC) { + // only increment this if we haven't reached the threshold yet. this is to avoid + // overflowing the counter. + incrementAccelerationNearlyGravityCount(); } } else { - // acceleration wasn't similar to this entity's gravity, reset the counter - _accelerationNearlyGravityCount = 0; + // acceleration wasn't similar to this entities gravity, so reset the went-ballistic counter + resetAccelerationNearlyGravityCount(); } // if this entity has been accelerated at close to gravity for a certain number of simulation-steps, let // the entity server's estimates include gravity. - if (_accelerationNearlyGravityCount >= STEPS_TO_DECIDE_BALLISTIC) { + if (getAccelerationNearlyGravityCount() >= STEPS_TO_DECIDE_BALLISTIC) { _entity->setAcceleration(_entity->getGravity()); } else { _entity->setAcceleration(glm::vec3(0.0f)); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index eb42d03b52..ac16ec6d5d 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,7 +29,7 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); - virtual void handleEasyChanges(uint32_t& flags) override; + virtual bool handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; /// \return PhysicsMotionType based on params set in EntityItem @@ -51,6 +51,10 @@ public: virtual uint32_t getIncomingDirtyFlags() override; virtual void clearIncomingDirtyFlags() override; + void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } + void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } + uint8_t getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } + virtual float getObjectRestitution() const override { return _entity->getRestitution(); } virtual float getObjectFriction() const override { return _entity->getFriction(); } virtual float getObjectLinearDamping() const override { return _entity->getDamping(); } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index e902758461..482c3146f8 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -38,11 +38,8 @@ const glm::vec3& ObjectMotionState::getWorldOffset() { return _worldOffset; } -// We init worldSimulationStep to 1 instead of 0 because we initialize _lastKineticStep to (worldSimulationStep - 1) -// so that the object starts moving on the first frame that it was set kinematic. -static uint32_t worldSimulationStep { 1 }; - // static +uint32_t worldSimulationStep = 0; void ObjectMotionState::setWorldSimulationStep(uint32_t step) { assert(step > worldSimulationStep); worldSimulationStep = step; @@ -167,7 +164,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } -void ObjectMotionState::handleEasyChanges(uint32_t& flags) { +bool ObjectMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); btVector3 newPosition = glmToBullet(getObjectPosition()); @@ -186,10 +183,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { worldTrans.setRotation(newRotation); } _body->setWorldTransform(worldTrans); - if (!(flags & HARD_DIRTY_PHYSICS_FLAGS) && _body->isStaticObject()) { - // force activate static body so its Aabb is updated later - _body->activate(true); - } } else if (flags & Simulation::DIRTY_ROTATION) { btTransform worldTrans = _body->getWorldTransform(); btQuaternion newRotation = glmToBullet(getObjectRotation()); @@ -199,10 +192,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { } worldTrans.setRotation(newRotation); _body->setWorldTransform(worldTrans); - if (!(flags & HARD_DIRTY_PHYSICS_FLAGS) && _body->isStaticObject()) { - // force activate static body so its Aabb is updated later - _body->activate(true); - } } if (flags & Simulation::DIRTY_LINEAR_VELOCITY) { @@ -243,6 +232,8 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_MASS) { updateBodyMassProperties(); } + + return true; } bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { @@ -301,10 +292,6 @@ void ObjectMotionState::updateBodyVelocities() { _body->setActivationState(ACTIVE_TAG); } -void ObjectMotionState::updateLastKinematicStep() { - _lastKinematicStep = ObjectMotionState::getWorldSimulationStep() - 1; -} - void ObjectMotionState::updateBodyMassProperties() { float mass = getMass(); btVector3 inertia(0.0f, 0.0f, 0.0f); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 44cb88721c..bb78eb12d6 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -50,12 +50,11 @@ const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TY Simulation::DIRTY_COLLISION_GROUP); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | - Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY | - Simulation::DIRTY_PHYSICS_ACTIVATION); - + Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY); // These are the set of incoming flags that the PhysicsEngine needs to hear about: -const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS); +const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | + Simulation::DIRTY_PHYSICS_ACTIVATION); // These are the outgoing flags that the PhysicsEngine can affect: const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES; @@ -81,12 +80,11 @@ public: ObjectMotionState(btCollisionShape* shape); ~ObjectMotionState(); - virtual void handleEasyChanges(uint32_t& flags); + virtual bool handleEasyChanges(uint32_t& flags); virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); void updateBodyMaterialProperties(); void updateBodyVelocities(); - void updateLastKinematicStep(); virtual void updateBodyMassProperties(); MotionStateType getType() const { return _type; } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index be80149c96..0040c19c3d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -50,13 +50,6 @@ void PhysicsEngine::init() { // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones _dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - - // By default Bullet will update the Aabb's of all objects every frame, even statics. - // This can waste CPU cycles so we configure Bullet to only update ACTIVE objects here. - // However, this means when a static object is moved we must manually update its Aabb - // in order for its broadphase collision queries to work correctly. Look at how we use - // _activeStaticBodies to track and update the Aabb's of moved static objects. - _dynamicsWorld->setForceUpdateAllAabbs(false); } } @@ -87,7 +80,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); motionState->updateBodyVelocities(); - motionState->updateLastKinematicStep(); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD); @@ -197,18 +189,12 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob stillNeedChange.push_back(object); } } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags); - object->clearIncomingDirtyFlags(); + if (object->handleEasyChanges(flags)) { + object->clearIncomingDirtyFlags(); + } else { + stillNeedChange.push_back(object); + } } - if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { - _activeStaticBodies.push_back(object->getRigidBody()); - } - } - // active static bodies have changed (in an Easy way) and need their Aabbs updated - // but we've configured Bullet to NOT update them automatically (for improved performance) - // so we must do it ourselves - for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { - _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]); } return stillNeedChange; } @@ -254,7 +240,6 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); if (_myAvatarController) { - BT_PROFILE("avatarController"); // TODO: move this stuff outside and in front of stepSimulation, because // the updateShapeIfNecessary() call needs info from MyAvatar and should // be done on the main thread during the pre-simulation stuff @@ -403,12 +388,6 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { const VectorOfMotionStates& PhysicsEngine::getOutgoingChanges() { BT_PROFILE("copyOutgoingChanges"); - // Bullet will not deactivate static objects (it doesn't expect them to be active) - // so we must deactivate them ourselves - for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { - _activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING); - } - _activeStaticBodies.clear(); _dynamicsWorld->synchronizeMotionStates(); _hasOutgoingChanges = false; return _dynamicsWorld->getChangedMotionStates(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 18e4016fec..f644d6f5b2 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -13,9 +13,9 @@ #define hifi_PhysicsEngine_h #include -#include #include +#include #include #include @@ -41,7 +41,7 @@ public: }; typedef std::map ContactMap; -typedef std::vector CollisionEvents; +typedef QVector CollisionEvents; class PhysicsEngine { public: @@ -110,7 +110,6 @@ private: ContactMap _contactMap; CollisionEvents _collisionEvents; QHash _objectActions; - std::vector _activeStaticBodies; glm::vec3 _originOffset; QUuid _sessionID; @@ -122,6 +121,7 @@ private: bool _dumpNextStats = false; bool _hasOutgoingChanges = false; + }; typedef std::shared_ptr PhysicsEnginePointer; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 5fe99f137c..b6f3487f1a 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -67,10 +67,7 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep saveKinematicState(fixedTimeStep*clampedSimulationSteps); - { - BT_PROFILE("applyGravity"); - applyGravity(); - } + applyGravity(); for (int i=0;igetActivationState() != ISLAND_SLEEPING) - { - if (body->isKinematicObject()) - { - //to calculate velocities next frame - body->saveKinematicState(timeStep); - } - } - } -} - - diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 68062d8d29..e9708149da 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -41,7 +41,6 @@ public: btScalar fixedTimeStep = btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); virtual void synchronizeMotionStates() override; - virtual void saveKinematicState(btScalar timeStep) override; // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 53abb3827d..d21d88d212 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -463,6 +463,14 @@ glm::vec2 getFacingDir2D(const glm::mat4& m) { } } +bool isNaN(glm::vec3 value) { + return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); +} + +bool isNaN(glm::quat value) { + return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); +} + glm::mat4 orthoInverse(const glm::mat4& m) { glm::mat4 r = m; r[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 8b1446d4e5..469ca1fc81 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -229,8 +229,8 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda glm::vec2 getFacingDir2D(const glm::quat& rot); glm::vec2 getFacingDir2D(const glm::mat4& m); -inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); } -inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); } +bool isNaN(glm::vec3 value); +bool isNaN(glm::quat value); glm::mat4 orthoInverse(const glm::mat4& m); diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index 794f338dc5..6d320e69cb 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -51,10 +51,10 @@ const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14; const int16_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS; // STATIC does not collide with itself (as optimization of physics simulation) -const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_STATIC); +const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_STATIC); const int16_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT; -const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC; +const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_DEFAULT; // MY_AVATAR does not collide with itself const int16_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index d48bddbd88..30d4726bcc 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -247,6 +247,12 @@ int getNthBit(unsigned char byte, int ordinal) { return ERROR_RESULT; } +bool isBetween(int64_t value, int64_t max, int64_t min) { + return ((value <= max) && (value >= min)); +} + + + void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { //assert(value <= 3 && value >= 0); byte |= ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 @@ -254,7 +260,12 @@ void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { bool isInEnvironment(const char* environment) { char* environmentString = getenv("HIFI_ENVIRONMENT"); - return (environmentString && strcmp(environmentString, environment) == 0); + + if (environmentString && strcmp(environmentString, environment) == 0) { + return true; + } else { + return false; + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -621,6 +632,10 @@ void debug::checkDeadBeef(void* memoryVoid, int size) { assert(memcmp((unsigned char*)memoryVoid, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0); } +bool isNaN(float value) { + return value != value; +} + QString formatUsecTime(float usecs, int prec) { static const quint64 SECONDS_PER_MINUTE = 60; static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index e9201b4a92..8fb65a5247 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -180,11 +180,11 @@ private: static int DEADBEEF_SIZE; }; -/// \return true when value is between max and min -inline bool isBetween(int64_t value, int64_t max, int64_t min) { return ((value <= max) && (value >= min)); } +bool isBetween(int64_t value, int64_t max, int64_t min); + /// \return bool is the float NaN -inline bool isNaN(float value) { return value != value; } +bool isNaN(float value); QString formatUsecTime(float usecs, int prec = 3); QString formatSecondsElapsed(float seconds); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 3cbd3c5ac9..13bf5d9054 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -90,9 +90,11 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons return parent; } + SpatiallyNestablePointer thisPointer = getThisPointer(); + if (parent) { // we have a parent pointer but our _parentID doesn't indicate this parent. - parent->forgetChild(getThisPointer()); + parent->forgetChild(thisPointer); _parentKnowsMe = false; _parent.reset(); } @@ -110,11 +112,16 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons parent = _parent.lock(); if (parent) { - parent->beParentOfChild(getThisPointer()); + parent->beParentOfChild(thisPointer); _parentKnowsMe = true; } - success = (parent || parentID.isNull()); + if (parent || parentID.isNull()) { + success = true; + } else { + success = false; + } + return parent; } @@ -842,40 +849,3 @@ AACube SpatiallyNestable::getQueryAACube() const { } return result; } - -void SpatiallyNestable::getLocalTransformAndVelocities( - Transform& transform, - glm::vec3& velocity, - glm::vec3& angularVelocity) const { - // transform - _transformLock.withReadLock([&] { - transform = _transform; - }); - // linear velocity - _velocityLock.withReadLock([&] { - velocity = _velocity; - }); - // angular velocity - _angularVelocityLock.withReadLock([&] { - angularVelocity = _angularVelocity; - }); -} - -void SpatiallyNestable::setLocalTransformAndVelocities( - const Transform& localTransform, - const glm::vec3& localVelocity, - const glm::vec3& localAngularVelocity) { - // transform - _transformLock.withWriteLock([&] { - _transform = localTransform; - }); - // linear velocity - _velocityLock.withWriteLock([&] { - _velocity = localVelocity; - }); - // angular velocity - _angularVelocityLock.withWriteLock([&] { - _angularVelocity = localAngularVelocity; - }); - locationChanged(); -} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index ef70d0231b..379f2facd7 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -149,13 +149,6 @@ protected: quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? SpatiallyNestablePointer getParentPointer(bool& success) const; - void getLocalTransformAndVelocities(Transform& localTransform, glm::vec3& localVelocity, glm::vec3& localAngularVelocity) const; - - void setLocalTransformAndVelocities( - const Transform& localTransform, - const glm::vec3& localVelocity, - const glm::vec3& localAngularVelocity); - mutable SpatiallyNestableWeakPointer _parent; virtual void beParentOfChild(SpatiallyNestablePointer newChild) const; diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index c51b3dae4b..a3a3c05731 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -150,3 +150,7 @@ QJsonObject Transform::toJson(const Transform& transform) { } return result; } + +bool Transform::containsNaN() const { + return isNaN(_rotation) || isNaN(_scale) || isNaN(_translation); +} diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 1e1d10c54b..1024173cbd 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -145,7 +145,7 @@ public: Vec4 transform(const Vec4& pos) const; Vec3 transform(const Vec3& pos) const; - bool containsNaN() const { return isNaN(_rotation) || isNaN(glm::dot(_scale, _translation)); } + bool containsNaN() const; protected: From c168e2cc5894be2d612b50f15d9bfebcb8e8de40 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 4 Apr 2016 13:37:18 -0700 Subject: [PATCH 22/23] PR feedback --- libraries/gpu/src/gpu/GLBackend.h | 6 ++-- libraries/gpu/src/gpu/GLBackendTexture.cpp | 23 +++++++-------- .../gpu/src/gpu/GLBackendTextureTransfer.cpp | 29 +++++++++++++++---- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 4f22a68631..f9fd299827 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -103,13 +103,13 @@ public: SyncState getSyncState() const { return _syncState; } // Is the storage out of date relative to the gpu texture? - bool invalid() const; + bool isInvalid() const; // Is the content out of date relative to the gpu texture? - bool outdated() const; + bool isOutdated() const; // Is the texture in a state where it can be rendered with no work? - bool ready() const; + bool isReady() const; // Move the image bits from the CPU to the GPU void transfer() const; diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 92123a68be..f38cc89a8c 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -19,8 +19,6 @@ using namespace gpu; GLenum gpuToGLTextureType(const Texture& texture) { - // If we get here, we need to allocate and or update the content of the texture - // or it's already being transferred switch (texture.getType()) { case Texture::TEX_2D: return GL_TEXTURE_2D; @@ -94,17 +92,17 @@ GLBackend::GLTexture::~GLTexture() { Backend::decrementTextureGPUCount(); } -bool GLBackend::GLTexture::invalid() const { +bool GLBackend::GLTexture::isInvalid() const { return _storageStamp < _gpuTexture.getStamp(); } -bool GLBackend::GLTexture::outdated() const { +bool GLBackend::GLTexture::isOutdated() const { return _contentStamp < _gpuTexture.getDataStamp(); } -bool GLBackend::GLTexture::ready() const { +bool GLBackend::GLTexture::isReady() const { // If we have an invalid texture, we're never ready - if (invalid()) { + if (isInvalid()) { return false; } @@ -112,7 +110,7 @@ bool GLBackend::GLTexture::ready() const { // as a special case auto syncState = _syncState.load(); - if (outdated()) { + if (isOutdated()) { return Pending == syncState; } @@ -167,7 +165,7 @@ void GLBackend::GLTexture::transfer() const { case Texture::TEX_CUBE: // transfer pixels from each faces - for (int f = 0; f < CUBE_NUM_FACES; f++) { + for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { if (_gpuTexture.isStoredMipFaceAvailable(0, f)) { transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f)); } @@ -188,15 +186,14 @@ void GLBackend::GLTexture::transfer() const { // Do any post-transfer operations that might be required on the main context / rendering thread void GLBackend::GLTexture::postTransfer() { setSyncState(GLTexture::Idle); + // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory switch (_gpuTexture.getType()) { case Texture::TEX_2D: - // At this point the mip piels have been loaded, we can notify _gpuTexture.notifyMipFaceGPULoaded(0, 0); break; case Texture::TEX_CUBE: for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) { - // At this point the mip piels have been loaded, we can notify _gpuTexture.notifyMipFaceGPULoaded(0, f); } break; @@ -216,7 +213,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin // If the object hasn't been created, or the object definition is out of date, drop and re-create GLTexture* object = Backend::getGPUObject(texture); - if (object && object->ready()) { + if (object && object->isReady()) { return object; } @@ -224,7 +221,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin // Create the texture if need be (force re-creation if the storage stamp changes // for easier use of immutable storage) - if (!object || object->invalid()) { + if (!object || object->isInvalid()) { // This automatically destroys the old texture object = new GLTexture(texture); } @@ -236,7 +233,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin // Object might be outdated, if so, start the transfer // (outdated objects that are already in transfer will have reported 'true' for ready() - if (object->outdated()) { + if (object->isOutdated()) { _textureTransferHelper->transferTexture(texturePointer); } diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp index aa5839b5b2..2610d9bc4a 100644 --- a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp @@ -12,12 +12,15 @@ #include "GLTexelFormat.h" #ifdef THREADED_TEXTURE_TRANSFER -#include -#include -#include -#endif -using namespace gpu; +#include +#include + +//#define FORCE_DRAW_AFTER_TRANSFER + +#ifdef FORCE_DRAW_AFTER_TRANSFER + +#include static ProgramPtr _program; static ProgramPtr _cubeProgram; @@ -25,6 +28,12 @@ static ShapeWrapperPtr _plane; static ShapeWrapperPtr _skybox; static BasicFramebufferWrapperPtr _framebuffer; +#endif + +#endif + +using namespace gpu; + GLTextureTransferHelper::GLTextureTransferHelper() { #ifdef THREADED_TEXTURE_TRANSFER _canvas = std::make_shared(); @@ -54,6 +63,8 @@ void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texture void GLTextureTransferHelper::setup() { #ifdef THREADED_TEXTURE_TRANSFER _canvas->makeCurrent(); + +#ifdef FORCE_DRAW_AFTER_TRANSFER _program = loadDefaultShader(); _plane = loadPlane(_program); _cubeProgram = loadCubemapShader(); @@ -62,6 +73,8 @@ void GLTextureTransferHelper::setup() { _framebuffer->Init({ 100, 100 }); _framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw); #endif + +#endif } bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { @@ -77,6 +90,7 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); object->transfer(); +#ifdef FORCE_DRAW_AFTER_TRANSFER // Now force a draw using the texture try { switch (texturePointer->getType()) { @@ -99,9 +113,12 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { } catch (const std::runtime_error& error) { qWarning() << "Failed to render texture on background thread: " << error.what(); } +#endif glBindTexture(object->_target, 0); - glFinish(); + auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + glDeleteSync(writeSync); object->_contentStamp = texturePointer->getDataStamp(); object->setSyncState(GLBackend::GLTexture::Transferred); } From ac30aed9482d9a4117b8d4c5a94b57a6ed8aa55f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 4 Apr 2016 17:52:41 -0700 Subject: [PATCH 23/23] Hold onto octree child after creation --- libraries/octree/src/Octree.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index ffbc2e6709..051e4f8791 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -384,9 +384,10 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig // check the exists mask to see if we have a child to traverse into if (oneAtBit(childInBufferMask, childIndex)) { - if (!destinationElement->getChildAtIndex(childIndex)) { + auto childAt = destinationElement->getChildAtIndex(childIndex); + if (!childAt) { // add a child at that index, if it doesn't exist - destinationElement->addChildAtIndex(childIndex); + childAt = destinationElement->addChildAtIndex(childIndex); bool nodeIsDirty = destinationElement->isDirty(); if (nodeIsDirty) { _isDirty = true; @@ -394,8 +395,7 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig } // tell the child to read the subsequent data - int lowerLevelBytes = readElementData(destinationElement->getChildAtIndex(childIndex), - nodeData + bytesRead, bytesLeftToRead, args); + int lowerLevelBytes = readElementData(childAt, nodeData + bytesRead, bytesLeftToRead, args); bytesRead += lowerLevelBytes; bytesLeftToRead -= lowerLevelBytes;