diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 43bc922c74..06fb5c4f47 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -327,6 +327,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url showStats = true; } else if (url.path() == "/resetStats") { _octreeInboundPacketProcessor->resetStats(); + _tree->resetEditStats(); resetSendingStats(); showStats = true; } @@ -627,6 +628,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // display inbound packet stats statsString += QString().sprintf("%s Edit Statistics... [RESET]\r\n", getMyServerName()); + quint64 currentPacketsInQueue = _octreeInboundPacketProcessor->packetsToProcessCount(); quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); @@ -635,8 +637,18 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url quint64 totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed(); quint64 totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed(); + quint64 averageDecodeTime = _tree->getAverageDecodeTime(); + quint64 averageLookupTime = _tree->getAverageLookupTime(); + quint64 averageUpdateTime = _tree->getAverageUpdateTime(); + quint64 averageCreateTime = _tree->getAverageCreateTime(); + quint64 averageLoggingTime = _tree->getAverageLoggingTime(); + + float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed; + statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n") + .arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Total Inbound Packets: %1 packets\r\n") .arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Total Inbound Elements: %1 elements\r\n") @@ -654,6 +666,17 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n") .arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Decode Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageDecodeTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Lookup Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageLookupTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Update Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageUpdateTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Create Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageCreateTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Logging Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageLoggingTime).rightJustified(COLUMN_WIDTH, ' ')); + int senderNumber = 0; NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats(); @@ -1411,6 +1434,8 @@ void OctreeServer::sendStatsPacket() { static QJsonObject statsObject3; + statsObject3[baseName + QString(".3.inbound.data.1.packetQueue")] = + (double)_octreeInboundPacketProcessor->packetsToProcessCount(); statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] = (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed(); statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] = diff --git a/examples/animationPerfTest.js b/examples/animationPerfTest.js new file mode 100644 index 0000000000..6bf310db23 --- /dev/null +++ b/examples/animationPerfTest.js @@ -0,0 +1,91 @@ +// +// Created by Bradley Austin Davis on 2015/07/01 +// Copyright 2015 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 +// + +var NUM_MOONS = 20; +// 1 = 60Hz, 2 = 30Hz, 3 = 20Hz, etc +var UPDATE_FREQUENCY_DIVISOR = 2; + +var MAX_RANGE = 75.0; +var LIFETIME = 600; +var SCALE = 0.1; + +var center = Vec3.sum(MyAvatar.position, + Vec3.multiply(MAX_RANGE * SCALE, Quat.getFront(Camera.getOrientation()))); + +var DEGREES_TO_RADIANS = Math.PI / 180.0; +var PARTICLE_MIN_SIZE = 2.50; +var PARTICLE_MAX_SIZE = 2.50; + + +var planet = Entities.addEntity({ + type: "Sphere", + position: center, + dimensions: { x: 10 * SCALE, y: 10 * SCALE, z: 10 * SCALE }, + color: { red: 0, green: 0, blue: 255 }, + ignoreCollisions: true, + collisionsWillMove: false, + lifetime: LIFETIME +}); + +var moons = []; + +// Create initial test particles that will move according to gravity from the planets +for (var i = 0; i < NUM_MOONS; i++) { + var radius = PARTICLE_MIN_SIZE + Math.random() * PARTICLE_MAX_SIZE; + radius *= SCALE; + var gray = Math.random() * 155; + var position = { x: 10 , y: i * 3, z: 0 }; + var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray }; + if (i == 0) { + color = { red: 255, green: 0, blue: 0 }; + radius = 6 * SCALE + } + moons.push(Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(center, position), + dimensions: { x: radius, y: radius, z: radius }, + color: color, + ignoreCollisions: true, + lifetime: LIFETIME, + collisionsWillMove: false + })); +} + +Script.update.connect(update); + +function scriptEnding() { + Entities.deleteEntity(planet); + for (var i = 0; i < moons.length; i++) { + Entities.deleteEntity(moons[i]); + } +} + +var totalTime = 0.0; +var updateCount = 0; +function update(deltaTime) { + // Apply gravitational force from planets + totalTime += deltaTime; + updateCount++; + if (0 != updateCount % UPDATE_FREQUENCY_DIVISOR) { + return; + } + + var planetProperties = Entities.getEntityProperties(planet); + var center = planetProperties.position; + var particlePos = Entities.getEntityProperties(moons[0]).position; + var relativePos = Vec3.subtract(particlePos.position, center); + for (var t = 0; t < moons.length; t++) { + var thetaDelta = (Math.PI * 2.0 / NUM_MOONS) * t; + var y = Math.sin(totalTime + thetaDelta) * 10.0 * SCALE; + var x = Math.cos(totalTime + thetaDelta) * 10.0 * SCALE; + var newBasePos = Vec3.sum({ x: 0, y: y, z: x }, center); + Entities.editEntity(moons[t], { position: newBasePos}); + } +} + +Script.scriptEnding.connect(scriptEnding); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d68a3813a9..ca264fd42f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,7 @@ using namespace std; // Starfield information static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; +static uint8_t THROTTLED_IDLE_TIMER_DELAY = 10; const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB @@ -1037,7 +1039,7 @@ void Application::showEditEntitiesHelp() { InfoView::show(INFO_EDIT_ENTITIES_PATH); } -void Application::resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size) { +void Application::resetCameras(Camera& camera, const glm::uvec2& size) { if (OculusManager::isConnected()) { OculusManager::configureCamera(camera); } else if (TV3DManager::isConnected()) { @@ -1060,7 +1062,6 @@ void Application::resizeGL() { if (_renderResolution != toGlm(renderSize)) { _renderResolution = toGlm(renderSize); DependencyManager::get()->setFrameBufferSize(renderSize); - resetCamerasOnResizeGL(_myCamera, _renderResolution); glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? @@ -1068,6 +1069,8 @@ void Application::resizeGL() { glLoadIdentity(); } + resetCameras(_myCamera, _renderResolution); + auto offscreenUi = DependencyManager::get(); auto canvasSize = _glWidget->size(); @@ -1784,8 +1787,22 @@ void Application::checkFPS() { } void Application::idle() { - PerformanceTimer perfTimer("idle"); + static SimpleAverage interIdleDurations; + static uint64_t lastIdleEnd{ 0 }; + if (lastIdleEnd != 0) { + uint64_t now = usecTimestampNow(); + interIdleDurations.update(now - lastIdleEnd); + static uint64_t lastReportTime = now; + if ((now - lastReportTime) >= (USECS_PER_SECOND)) { + static QString LOGLINE("Average inter-idle time: %1 us for %2 samples"); + qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount()); + interIdleDurations.reset(); + lastReportTime = now; + } + } + + PerformanceTimer perfTimer("idle"); if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -1828,13 +1845,15 @@ void Application::idle() { _idleLoopStdev.reset(); } - // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. - idleTimer->start(2); } - } + // After finishing all of the above work, ensure the idle timer is set to the proper interval, + // depending on whether we're throttling or not + idleTimer->start(_glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : 0); + } // check for any requested background downloads. emit checkBackgroundDownloads(); + lastIdleEnd = usecTimestampNow(); } void Application::setFullscreen(bool fullscreen) { @@ -3542,7 +3561,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } //Render the sixense lasers if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { - _myAvatar->renderLaserPointers(); + _myAvatar->renderLaserPointers(*renderArgs->_batch); } if (!selfAvatarOnly) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 375aded8ac..1d3c0dcc70 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -483,7 +483,7 @@ private slots: void setCursorVisible(bool visible); private: - void resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size); + void resetCameras(Camera& camera, const glm::uvec2& size); void updateProjectionMatrix(); void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); diff --git a/interface/src/InterfaceLogging.cpp b/interface/src/InterfaceLogging.cpp index 18bc4e58e8..0afcb30c27 100644 --- a/interface/src/InterfaceLogging.cpp +++ b/interface/src/InterfaceLogging.cpp @@ -12,3 +12,4 @@ #include "InterfaceLogging.h" Q_LOGGING_CATEGORY(interfaceapp, "hifi.interface") +Q_LOGGING_CATEGORY(interfaceapp_timing, "hifi.interface.timing") diff --git a/interface/src/InterfaceLogging.h b/interface/src/InterfaceLogging.h index d1d92aa93d..be2ee73fba 100644 --- a/interface/src/InterfaceLogging.h +++ b/interface/src/InterfaceLogging.h @@ -15,5 +15,6 @@ #include Q_DECLARE_LOGGING_CATEGORY(interfaceapp) +Q_DECLARE_LOGGING_CATEGORY(interfaceapp_timing) #endif // hifi_InterfaceLogging_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d0778481a6..63b08b693c 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -449,26 +449,26 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } if (renderBounding && shouldRenderHead(renderArgs)) { - _skeletonModel.renderBoundingCollisionShapes(0.7f); + _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } + } - // If this is the avatar being looked at, render a little ball above their head - if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) { - const float LOOK_AT_INDICATOR_RADIUS = 0.03f; - const float LOOK_AT_INDICATOR_OFFSET = 0.22f; - const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; - glm::vec3 position; - if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { - position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z); - } else { - position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z); - } - Transform transform; - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS - , 15, 15, LOOK_AT_INDICATOR_COLOR); + // If this is the avatar being looked at, render a little ball above their head + if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) { + const float LOOK_AT_INDICATOR_RADIUS = 0.03f; + const float LOOK_AT_INDICATOR_OFFSET = 0.22f; + const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; + glm::vec3 position; + if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { + position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z); + } else { + position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z); } + Transform transform; + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS + , 15, 15, LOOK_AT_INDICATOR_COLOR); } // quick check before falling into the code below: @@ -1010,7 +1010,7 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) { int Avatar::_jointConesID = GeometryCache::UNKNOWN_ID; // render a makeshift cone section that serves as a body part connecting joint spheres -void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, +void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2, float radius1, float radius2, const glm::vec4& color) { auto geometryCache = DependencyManager::get(); @@ -1057,7 +1057,7 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, // TODO: this is really inefficient constantly recreating these vertices buffers. It would be // better if the avatars cached these buffers for each of the joints they are rendering geometryCache->updateVertices(_jointConesID, points, color); - geometryCache->renderVertices(gpu::TRIANGLES, _jointConesID); + geometryCache->renderVertices(batch, gpu::TRIANGLES, _jointConesID); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 236b04864b..b23059acb0 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -148,7 +148,7 @@ public: virtual int parseDataAtOffset(const QByteArray& packet, int offset); - static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, + static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2, float radius1, float radius2, const glm::vec4& color); virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 74653d9768..7b2968973c 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -103,7 +103,8 @@ void Hand::resolvePenetrations() { } void Hand::render(RenderArgs* renderArgs, bool isMine) { - if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && + gpu::Batch& batch = *renderArgs->_batch; + if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { // draw a green sphere at hand joint location, which is actually near the wrist) for (size_t i = 0; i < getNumPalms(); i++) { @@ -112,31 +113,25 @@ void Hand::render(RenderArgs* renderArgs, bool isMine) { continue; } glm::vec3 position = palm.getPosition(); - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - DependencyManager::get()->renderSphere(PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10, glm::vec3(0.0f, 1.0f, 0.0f)); - glPopMatrix(); + Transform transform = Transform(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSphere(batch, PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10, glm::vec3(0.0f, 1.0f, 0.0f)); } } if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) { - renderHandTargets(isMine); + renderHandTargets(renderArgs, isMine); } - glEnable(GL_DEPTH_TEST); - glEnable(GL_RESCALE_NORMAL); -} - -void Hand::renderHandTargets(bool isMine) { - glPushMatrix(); +} +void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { + gpu::Batch& batch = *renderArgs->_batch; const float avatarScale = DependencyManager::get()->getMyAvatar()->getScale(); const float alpha = 1.0f; const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin - - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); if (isMine && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) { for (size_t i = 0; i < getNumPalms(); ++i) { @@ -145,12 +140,12 @@ void Hand::renderHandTargets(bool isMine) { continue; } glm::vec3 targetPosition = palm.getTipPosition(); - glPushMatrix(); - glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z); + Transform transform = Transform(); + transform.setTranslation(targetPosition); + batch.setModelTransform(transform); const float collisionRadius = 0.05f; - DependencyManager::get()->renderSphere(collisionRadius, 10, 10, glm::vec4(0.5f,0.5f,0.5f, alpha), false); - glPopMatrix(); + DependencyManager::get()->renderSphere(batch, collisionRadius, 10, 10, glm::vec4(0.5f,0.5f,0.5f, alpha), false); } } @@ -165,22 +160,19 @@ void Hand::renderHandTargets(bool isMine) { if (palm.isActive()) { glm::vec3 tip = palm.getTipPosition(); glm::vec3 root = palm.getPosition(); - - Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); + Transform transform = Transform(); + transform.setTranslation(glm::vec3()); + batch.setModelTransform(transform); + Avatar::renderJointConnectingCone(batch, root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); // Render sphere at palm/finger root glm::vec3 offsetFromPalm = root + palm.getNormal() * PALM_DISK_THICKNESS; - Avatar::renderJointConnectingCone(root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); - glPushMatrix(); - glTranslatef(root.x, root.y, root.z); - DependencyManager::get()->renderSphere(PALM_BALL_RADIUS, 20.0f, 20.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); - glPopMatrix(); + Avatar::renderJointConnectingCone(batch, root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); + transform = Transform(); + transform.setTranslation(root); + batch.setModelTransform(transform); + DependencyManager::get()->renderSphere(batch, PALM_BALL_RADIUS, 20.0f, 20.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); } } - - glDepthMask(GL_TRUE); - glEnable(GL_DEPTH_TEST); - - glPopMatrix(); } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index cb35497960..f6991c5a55 100644 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -56,7 +56,7 @@ private: Avatar* _owningAvatar; - void renderHandTargets(bool isMine); + void renderHandTargets(RenderArgs* renderArgs, bool isMine); }; #endif // hifi_Hand_h diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 27888b9d4e..911bd4f4a4 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -9,9 +9,11 @@ // #include +#include +#include #include -#include +#include #include #include "Application.h" @@ -293,10 +295,8 @@ void Head::relaxLean(float deltaTime) { } void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) { - if (postLighting) { - if (_renderLookatVectors) { + if (_renderLookatVectors) { renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getLookAtPosition()); - } } } @@ -367,17 +367,19 @@ void Head::addLeanDeltas(float sideways, float forward) { } void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { + auto& batch = *renderArgs->_batch; + auto transform = Transform{}; + batch.setModelTransform(transform); + batch._glLineWidth(2.0f); + + auto deferredLighting = DependencyManager::get(); + deferredLighting->bindSimpleProgram(batch); + auto geometryCache = DependencyManager::get(); - DependencyManager::get()->begin(renderArgs); - - glLineWidth(2.0); - glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f); glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f); - geometryCache->renderLine(leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID); - geometryCache->renderLine(rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); - - DependencyManager::get()->end(renderArgs); + geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID); + geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 170fc03d17..03b8a56526 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1208,9 +1208,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo if (shouldRenderHead(renderArgs)) { getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } - if (postLighting) { - getHand()->render(renderArgs, true); - } + getHand()->render(renderArgs, true); } void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) { @@ -1584,7 +1582,7 @@ void MyAvatar::updateMotionBehavior() { } //Renders sixense laser pointers for UI selection with controllers -void MyAvatar::renderLaserPointers() { +void MyAvatar::renderLaserPointers(gpu::Batch& batch) { const float PALM_TIP_ROD_RADIUS = 0.002f; //If the Oculus is enabled, we will draw a blue cursor ray @@ -1597,8 +1595,10 @@ void MyAvatar::renderLaserPointers() { //Scale the root vector with the avatar scale scaleVectorRelativeToPosition(root); - - Avatar::renderJointConnectingCone(root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1)); + Transform transform = Transform(); + transform.setTranslation(glm::vec3()); + batch.setModelTransform(transform); + Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1)); } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index daec7d3457..d09cff1394 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -163,7 +163,7 @@ public: bool allowDuplicates = false, bool useSaved = true); /// Renders a laser pointer for UI picking - void renderLaserPointers(); + void renderLaserPointers(gpu::Batch& batch); glm::vec3 getLaserPointerTipPosition(const PalmData* palm); const RecorderPointer getRecorder() const { return _recorder; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 2f66e84b50..20d458195d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -776,24 +776,24 @@ void SkeletonModel::resetShapePositionsToDefaultPose() { _boundingShape.setRotation(_rotation); } -void SkeletonModel::renderBoundingCollisionShapes(float alpha) { +void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { const int BALL_SUBDIVISIONS = 10; if (_shapes.isEmpty()) { // the bounding shape has not been propery computed // so no need to render it return; } - glPushMatrix(); - Application::getInstance()->loadTranslatedViewMatrix(_translation); // draw a blue sphere at the capsule endpoint glm::vec3 endPoint; _boundingShape.getEndPoint(endPoint); endPoint = endPoint - _translation; - glTranslatef(endPoint.x, endPoint.y, endPoint.z); + Transform transform = Transform(); + transform.setTranslation(endPoint); + batch.setModelTransform(transform); auto geometryCache = DependencyManager::get(); - geometryCache->renderSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); + geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); // draw a yellow sphere at the capsule startpoint glm::vec3 startPoint; @@ -805,9 +805,7 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) { // draw a green cylinder between the two points glm::vec3 origin(0.0f); - Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); - - glPopMatrix(); + Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); } bool SkeletonModel::hasSkeleton() { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index bffdc58659..755aa3dfee 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -101,7 +101,7 @@ public: const glm::vec3& getStandingOffset() const { return _standingOffset; } void computeBoundingShape(const FBXGeometry& geometry); - void renderBoundingCollisionShapes(float alpha); + void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha); float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } const CapsuleShape& getBoundingShape() const { return _boundingShape; } const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; } diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index eca250a428..93b3ef8d07 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -221,8 +221,6 @@ void PreferencesDialog::savePreferences() { myAvatar->setLeanScale(ui.leanScaleSpin->value()); myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value()); - Application::getInstance()->resizeGL(); - DependencyManager::get()->getMyAvatar()->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value()); qApp->setFieldOfView(ui.fieldOfViewSpin->value()); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index ccad3bd295..3f033d9266 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -170,3 +170,11 @@ QSizeF TextOverlay::textSize(const QString& text) const { return QSizeF(extents.x, extents.y); } + +void TextOverlay::setFontSize(int fontSize) { + _fontSize = fontSize; + + auto oldTextRenderer = _textRenderer; + _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); + delete oldTextRenderer; +} diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index de2597cf9a..32786c3220 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -48,7 +48,7 @@ public: void setText(const QString& text) { _text = text; } void setLeftMargin(int margin) { _leftMargin = margin; } void setTopMargin(int margin) { _topMargin = margin; } - void setFontSize(int fontSize) { _fontSize = fontSize; } + void setFontSize(int fontSize); virtual void setProperties(const QScriptValue& properties); virtual TextOverlay* createClone() const; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 752082932b..6a652d609b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -574,41 +574,61 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char case PacketTypeEntityAdd: case PacketTypeEntityEdit: { + quint64 startDecode = 0, endDecode = 0; + quint64 startLookup = 0, endLookup = 0; + quint64 startUpdate = 0, endUpdate = 0; + quint64 startCreate = 0, endCreate = 0; + quint64 startLogging = 0, endLogging = 0; + + _totalEditMessages++; + EntityItemID entityItemID; EntityItemProperties properties; + startDecode = usecTimestampNow(); bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties); + endDecode = usecTimestampNow(); // If we got a valid edit packet, then it could be a new entity or it could be an update to // an existing entity... handle appropriately if (validEditPacket) { // search for the entity by EntityItemID + startLookup = usecTimestampNow(); EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID); + endLookup = usecTimestampNow(); if (existingEntity && packetType == PacketTypeEntityEdit) { // if the EntityItem exists, then update it + startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; qCDebug(entities) << " properties:" << properties; } + endLogging = usecTimestampNow(); + + startUpdate = usecTimestampNow(); updateEntity(entityItemID, properties, senderNode); existingEntity->markAsChangedOnServer(); + endUpdate = usecTimestampNow(); + _totalUpdates++; } else if (packetType == PacketTypeEntityAdd) { if (senderNode->getCanRez()) { // this is a new entity... assign a new entityID - if (wantEditLogging()) { - qCDebug(entities) << "User [" << senderNode->getUUID() << "] adding entity."; - qCDebug(entities) << " properties:" << properties; - } properties.setCreated(properties.getLastEdited()); + startCreate = usecTimestampNow(); EntityItemPointer newEntity = addEntity(entityItemID, properties); + endCreate = usecTimestampNow(); + _totalCreates++; if (newEntity) { newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); + + startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:" << newEntity->getEntityItemID(); qCDebug(entities) << " properties:" << properties; } + endLogging = usecTimestampNow(); } } else { @@ -619,6 +639,14 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity.get(); } } + + + _totalDecodeTime += endDecode - startDecode; + _totalLookupTime += endLookup - startLookup; + _totalUpdateTime += endUpdate - startUpdate; + _totalCreateTime += endCreate - startCreate; + _totalLoggingTime += endLogging - startLogging; + break; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 9c4be9a86f..fa72cc7691 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -168,6 +168,23 @@ public: float getContentsLargestDimension(); + virtual void resetEditStats() { + _totalEditMessages = 0; + _totalUpdates = 0; + _totalCreates = 0; + _totalDecodeTime = 0; + _totalLookupTime = 0; + _totalUpdateTime = 0; + _totalCreateTime = 0; + _totalLoggingTime = 0; + } + + virtual quint64 getAverageDecodeTime() const { return _totalEditMessages == 0 ? 0 : _totalDecodeTime / _totalEditMessages; } + virtual quint64 getAverageLookupTime() const { return _totalEditMessages == 0 ? 0 : _totalLookupTime / _totalEditMessages; } + virtual quint64 getAverageUpdateTime() const { return _totalUpdates == 0 ? 0 : _totalUpdateTime / _totalUpdates; } + virtual quint64 getAverageCreateTime() const { return _totalCreates == 0 ? 0 : _totalCreateTime / _totalCreates; } + virtual quint64 getAverageLoggingTime() const { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; } + signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); @@ -202,6 +219,17 @@ private: bool _wantEditLogging = false; void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL); + + + // some performance tracking properties - only used in server trees + int _totalEditMessages = 0; + int _totalUpdates = 0; + int _totalCreates = 0; + quint64 _totalDecodeTime = 0; + quint64 _totalLookupTime = 0; + quint64 _totalUpdateTime = 0; + quint64 _totalCreateTime = 0; + quint64 _totalLoggingTime = 0; }; #endif // hifi_EntityTree_h diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 894a7b8aa9..3c4b32b4ec 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -38,19 +38,28 @@ bool ReceivedPacketProcessor::process() { _hasPackets.wait(&_waitingOnPacketsMutex, getMaxWait()); _waitingOnPacketsMutex.unlock(); } + preProcess(); - while (_packets.size() > 0) { - lock(); // lock to make sure nothing changes on us - NetworkPacket& packet = _packets.front(); // get the oldest packet - NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us - _packets.erase(_packets.begin()); // remove the oldest packet - if (!temporary.getNode().isNull()) { - _nodePacketCounts[temporary.getNode()->getUUID()]--; - } - unlock(); // let others add to the packets - processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy + if (!_packets.size()) { + return isStillRunning(); + } + + lock(); + QVector currentPackets; + currentPackets.swap(_packets); + unlock(); + + foreach(auto& packet, currentPackets) { + processPacket(packet.getNode(), packet.getByteArray()); midProcess(); } + + lock(); + foreach(auto& packet, currentPackets) { + _nodePacketCounts[packet.getNode()->getUUID()]--; + } + unlock(); + postProcess(); return isStillRunning(); // keep running till they terminate us } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index d7fc58699f..6eeb423ddd 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -367,8 +367,16 @@ public: bool getIsClient() const { return !_isServer; } /// Is this a client based tree. Allows guards for certain operations void setIsClient(bool isClient) { _isServer = !isClient; } - virtual void dumpTree() { }; - virtual void pruneTree() { }; + virtual void dumpTree() { } + virtual void pruneTree() { } + + virtual void resetEditStats() { } + virtual quint64 getAverageDecodeTime() const { return 0; } + virtual quint64 getAverageLookupTime() const { return 0; } + virtual quint64 getAverageUpdateTime() const { return 0; } + virtual quint64 getAverageCreateTime() const { return 0; } + virtual quint64 getAverageLoggingTime() const { return 0; } + signals: void importSize(float x, float y, float z); diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index c14fd33565..b60ffc0891 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include "PathUtils.h" @@ -53,7 +54,15 @@ namespace Setting { privateInstance = new Manager(); Q_CHECK_PTR(privateInstance); - + + // Delete Interface.ini.lock file if it exists, otherwise Interface freezes. + QString settingsLockFilename = privateInstance->fileName() + ".lock"; + QFile settingsLockFile(settingsLockFilename); + if (settingsLockFile.exists()) { + bool deleted = settingsLockFile.remove(); + qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename; + } + QObject::connect(privateInstance, SIGNAL(destroyed()), thread, SLOT(quit())); QObject::connect(thread, SIGNAL(started()), privateInstance, SLOT(startTimer())); QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); diff --git a/libraries/shared/src/SimpleAverage.h b/libraries/shared/src/SimpleAverage.h new file mode 100644 index 0000000000..33ed9d84cc --- /dev/null +++ b/libraries/shared/src/SimpleAverage.h @@ -0,0 +1,33 @@ +// +// Created by Bradley Austin Davis on 2015/07/01. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_SimpleAverage_h +#define hifi_SimpleAverage_h + +template +class SimpleAverage { +public: + void update(T sample) { + _sum += sample; + ++_count; + } + void reset() { + _sum = 0; + _count = 0; + } + + int getCount() const { return _count; }; + T getAverage() const { return _sum / (T)_count; }; + +private: + int _count; + T _sum; +}; + +#endif