Merge branch 'master' of https://github.com/highfidelity/hifi into yellow

This commit is contained in:
Sam Gateau 2015-07-06 15:04:02 -07:00
commit fe633cd545
26 changed files with 318 additions and 1095 deletions

View file

@ -760,47 +760,6 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
statsString += QString(" Total: %1 nodes\r\n")
.arg(locale.toString((uint)checkSum).rightJustified(16, ' '));
#ifdef BLENDED_UNION_CHILDREN
statsString += "\r\n";
statsString += "OctreeElement Children Encoding Statistics...\r\n";
statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
OctreeElement::getSingleChildrenCount(),
((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT));
statsString += QString().sprintf(" Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
OctreeElement::getTwoChildrenOffsetCount(),
((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT));
statsString += QString().sprintf(" Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
OctreeElement::getTwoChildrenExternalCount(),
((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
statsString += QString().sprintf(" Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
OctreeElement::getThreeChildrenOffsetCount(),
((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
statsString += QString().sprintf(" Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
OctreeElement::getThreeChildrenExternalCount(),
((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
OctreeElement::getExternalChildrenCount(),
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
checkSum = OctreeElement::getSingleChildrenCount() +
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() +
OctreeElement::getExternalChildrenCount();
statsString += " ----------------\r\n";
statsString += QString().sprintf(" Total: %10.llu nodes\r\n", checkSum);
statsString += QString().sprintf(" Expected: %10.lu nodes\r\n", nodeCount);
statsString += "\r\n";
statsString += "In other news....\r\n";
statsString += QString().sprintf("could store 4 children internally: %10.llu nodes\r\n",
OctreeElement::getCouldStoreFourChildrenInternally());
statsString += QString().sprintf("could NOT store 4 children internally: %10.llu nodes\r\n",
OctreeElement::getCouldNotStoreFourChildrenInternally());
#endif
statsString += "\r\n\r\n";
statsString += "</pre>\r\n";
statsString += "</doc></html>";

View file

@ -531,6 +531,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats);
auto audioIO = DependencyManager::get<AudioClient>();
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");

View file

@ -269,6 +269,7 @@ namespace MenuOption {
const QString ShowDSConnectTable = "Show Domain Connection Timing";
const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowIKConstraints = "Show IK Constraints";
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
const QString SimpleShadows = "Simple";
const QString SixenseEnabled = "Enable Hydra Support";
const QString SixenseMouseInput = "Enable Sixense Mouse Input";

View file

@ -25,18 +25,13 @@
// Used to animate the magnification windows
static const float MAG_SPEED = 0.08f;
static const quint64 MSECS_TO_USECS = 1000ULL;
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
static const float reticleSize = TWO_PI / 100.0f;
static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
static const float CURSOR_PIXEL_SIZE = 32.0f;
static const float MOUSE_PITCH_RANGE = 1.0f * PI;
static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;

View file

@ -36,9 +36,6 @@
#include "ui/Stats.h"
#include "ui/AvatarInputs.h"
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
const int AUDIO_METER_GAP = 5;
const int MUTE_ICON_PADDING = 10;
const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
static const float ORTHO_NEAR_CLIP = -10000;

View file

@ -25,7 +25,9 @@
OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
_model(model) {
_model(model),
_averageUpdatesPerSecond(SAMPLES_PER_SECOND)
{
_statCount = 0;
_octreeServerLabelsCount = 0;
@ -50,6 +52,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
_localElements = AddStatItem("Local Elements");
_localElementsMemory = AddStatItem("Elements Memory");
_sendingMode = AddStatItem("Sending Mode");
_processedPackets = AddStatItem("Processed Packets");
_processedPacketsElements = AddStatItem("Processed Packets Elements");
_processedPacketsEntities = AddStatItem("Processed Packets Entities");
_processedPacketsTiming = AddStatItem("Processed Packets Timing");
_entityUpdateTime = AddStatItem("Entity Update Time");
_entityUpdates = AddStatItem("Entity Updates");
layout()->setSizeConstraint(QLayout::SetFixedSize);
}
@ -119,6 +129,34 @@ OctreeStatsDialog::~OctreeStatsDialog() {
void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
// Processed Entities Related stats
auto entities = Application::getInstance()->getEntities();
auto entitiesTree = entities->getTree();
// Do this ever paint event... even if we don't update
auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits();
// track our updated per second
const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND;
quint64 now = usecTimestampNow();
quint64 sinceLastWindow = now - _lastWindowAt;
auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits;
float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND;
float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds;
if (sinceLastWindow > SAMPLING_WINDOW) {
_averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond);
_lastWindowAt = now;
_lastKnownTrackedEdits = totalTrackedEdits;
}
// Only refresh our stats every once in a while, unless asked for realtime
quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND;
quint64 sinceLastRefresh = now - _lastRefresh;
if (sinceLastRefresh < REFRESH_AFTER) {
return QDialog::paintEvent(event);
}
_lastRefresh = now;
// Update labels
QLabel* label;
@ -203,9 +241,100 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
"Leaves: " << qPrintable(serversLeavesString) << "";
label->setText(statsValue.str().c_str());
// Processed Packets Elements
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
auto averagePacketsPerSecond = entities->getAveragePacketsPerSecond();
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket();
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket);
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket);
QString averagePacketsPerSecondString = locale.toString(averagePacketsPerSecond);
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond);
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond);
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
label = _labels[_processedPackets];
statsValue.str("");
statsValue <<
"" << qPrintable(averagePacketsPerSecondString) << " per second";
label->setText(statsValue.str().c_str());
label = _labels[_processedPacketsElements];
statsValue.str("");
statsValue <<
"" << qPrintable(averageElementsPerPacketString) << " per packet / " <<
"" << qPrintable(averageElementsPerSecondString) << " per second";
label->setText(statsValue.str().c_str());
label = _labels[_processedPacketsEntities];
statsValue.str("");
statsValue <<
"" << qPrintable(averageEntitiesPerPacketString) << " per packet / " <<
"" << qPrintable(averageEntitiesPerSecondString) << " per second";
label->setText(statsValue.str().c_str());
label = _labels[_processedPacketsTiming];
statsValue.str("");
statsValue <<
"Lock Wait:" << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " <<
"Uncompress:" << qPrintable(averageUncompressPerPacketString) << " (usecs) / " <<
"Process:" << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)";
label->setText(statsValue.str().c_str());
// Entity Edits update time
label = _labels[_entityUpdateTime];
auto averageEditDelta = entitiesTree->getAverageEditDeltas();
auto maxEditDelta = entitiesTree->getMaxEditDelta();
QString averageEditDeltaString = locale.toString((uint)averageEditDelta);
QString maxEditDeltaString = locale.toString((uint)maxEditDelta);
statsValue.str("");
statsValue <<
"Average: " << qPrintable(averageEditDeltaString) << " (usecs) / " <<
"Max: " << qPrintable(maxEditDeltaString) << " (usecs)";
label->setText(statsValue.str().c_str());
// Entity Edits
label = _labels[_entityUpdates];
auto bytesPerEdit = entitiesTree->getAverageEditBytes();
auto updatesPerSecond = _averageUpdatesPerSecond.getAverage();
if (updatesPerSecond < 1) {
updatesPerSecond = 0; // we don't really care about small updates per second so suppress those
}
QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits);
QString updatesPerSecondString = locale.toString(updatesPerSecond);
QString bytesPerEditString = locale.toString(bytesPerEdit);
statsValue.str("");
statsValue <<
"" << qPrintable(updatesPerSecondString) << " updates per second / " <<
"" << qPrintable(totalTrackedEditsString) << " total updates / " <<
"Average Size: " << qPrintable(bytesPerEditString) << " bytes ";
label->setText(statsValue.str().c_str());
showAllOctreeServers();
this->QDialog::paintEvent(event);
QDialog::paintEvent(event);
}
void OctreeStatsDialog::showAllOctreeServers() {
int serverCount = 0;

View file

@ -63,6 +63,21 @@ private:
int _serverElements;
int _localElements;
int _localElementsMemory;
int _entityUpdateTime;
int _entityUpdates;
int _processedPackets;
int _processedPacketsElements;
int _processedPacketsEntities;
int _processedPacketsTiming;
const int SAMPLES_PER_SECOND = 10;
SimpleMovingAverage _averageUpdatesPerSecond;
quint64 _lastWindowAt = 0;
quint64 _lastKnownTrackedEdits = 0;
quint64 _lastRefresh = 0;
int _octreeServerLables[MAX_VOXEL_SERVERS];
int _octreeServerLabelsCount;
details _extraServerDetails[MAX_VOXEL_SERVERS];

View file

@ -93,31 +93,6 @@ void BillboardOverlay::render(RenderArgs* args) {
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me
} else {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glBindTexture(GL_TEXTURE_2D, _texture->getID());
glPushMatrix(); {
glTranslatef(getPosition().x, getPosition().y, getPosition().z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glScalef(_dimensions.x, _dimensions.y, 1.0f);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
} glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glDisable(GL_ALPHA_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
}
}

View file

@ -89,75 +89,6 @@ void Grid3DOverlay::render(RenderArgs* args) {
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
}
} else {
if (!_gridProgram.isLinked()) {
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) {
qDebug() << "Failed to compile: " + _gridProgram.log();
return;
}
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) {
qDebug() << "Failed to compile: " + _gridProgram.log();
return;
}
if (!_gridProgram.link()) {
qDebug() << "Failed to link: " + _gridProgram.log();
return;
}
}
// Render code largely taken from MetavoxelEditor::render()
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glPushMatrix();
glm::quat rotation = getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glLineWidth(1.5f);
glm::vec3 position = getPosition();
_gridProgram.bind();
// Minor grid
glPushMatrix();
{
glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z);
float scale = MINOR_GRID_DIVISIONS * spacing;
glScalef(scale, scale, scale);
DependencyManager::get<GeometryCache>()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor);
}
glPopMatrix();
// Major grid
glPushMatrix();
{
glLineWidth(4.0f);
spacing *= _majorGridEvery;
glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z);
float scale = MAJOR_GRID_DIVISIONS * spacing;
glScalef(scale, scale, scale);
DependencyManager::get<GeometryCache>()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
}
glPopMatrix();
_gridProgram.release();
glPopMatrix();
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
}
}

View file

@ -32,25 +32,25 @@ void LocalModelsOverlay::update(float deltatime) {
void LocalModelsOverlay::render(RenderArgs* args) {
if (_visible) {
float glowLevel = getGlowLevel();
Glower* glower = NULL;
if (glowLevel > 0.0f) {
glower = new Glower(glowLevel);
}
glPushMatrix(); {
Application* app = Application::getInstance();
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
app->setViewMatrixTranslation(oldTranslation + getPosition());
_entityTreeRenderer->render(args);
Application::getInstance()->setViewMatrixTranslation(oldTranslation);
} glPopMatrix();
auto batch = args ->_batch;
Application* app = Application::getInstance();
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
Transform transform = Transform();
transform.setTranslation(oldTranslation + getPosition());
batch->setViewTransform(transform);
_entityTreeRenderer->render(args);
transform.setTranslation(oldTranslation);
batch->setViewTransform(transform);
if (glower) {
delete glower;
}
}
}

View file

@ -16,6 +16,7 @@
#include <Application.h>
#include <render/Scene.h>
#include <gpu/GLBackend.h>
#include "BillboardOverlay.h"
#include "Circle3DOverlay.h"
@ -96,6 +97,10 @@ void Overlays::cleanupOverlaysToDelete() {
void Overlays::renderHUD(RenderArgs* renderArgs) {
QReadLocker lock(&_lock);
gpu::Batch batch;
renderArgs->_batch = &batch;
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
if (thisOverlay->is3D()) {
glEnable(GL_DEPTH_TEST);
@ -109,6 +114,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
thisOverlay->render(renderArgs);
}
}
gpu::GLBackend::renderBatch(batch, true);
}
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {

View file

@ -62,19 +62,19 @@ namespace render {
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
if (args) {
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
glPushMatrix();
auto batch = args->_batch;
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat myAvatarRotation = avatar->getOrientation();
glm::vec3 myAvatarPosition = avatar->getPosition();
float angle = glm::degrees(glm::angle(myAvatarRotation));
glm::vec3 axis = glm::axis(myAvatarRotation);
float myAvatarScale = avatar->getScale();
glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z);
glRotatef(angle, axis.x, axis.y, axis.z);
glScalef(myAvatarScale, myAvatarScale, myAvatarScale);
Transform transform = Transform();
transform.setTranslation(myAvatarPosition);
transform.setRotation(glm::angleAxis(angle, axis));
transform.setScale(myAvatarScale);
batch->setModelTransform(transform);
overlay->render(args);
glPopMatrix();
} else {
overlay->render(args);
}

View file

@ -87,70 +87,6 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID);
}
}
} else {
glDisable(GL_LIGHTING);
float glowLevel = getGlowLevel();
Glower* glower = NULL;
if (glowLevel > 0.0f) {
glower = new Glower(glowLevel);
}
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
//glScalef(dimensions.x, dimensions.y, 1.0f);
glLineWidth(_lineWidth);
auto geometryCache = DependencyManager::get<GeometryCache>();
// for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line...
if (getIsSolid()) {
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, rectangleColor);
} else {
if (getIsDashedLine()) {
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
geometryCache->renderDashedLine(point1, point2, rectangleColor);
geometryCache->renderDashedLine(point2, point3, rectangleColor);
geometryCache->renderDashedLine(point3, point4, rectangleColor);
geometryCache->renderDashedLine(point4, point1, rectangleColor);
} else {
if (halfDimensions != _previousHalfDimensions) {
QVector<glm::vec3> border;
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f);
border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f);
border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f);
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
geometryCache->updateVertices(_geometryCacheID, border, rectangleColor);
_previousHalfDimensions = halfDimensions;
}
geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID);
}
}
glPopMatrix();
glPopMatrix();
if (glower) {
delete glower;
}
}
}

View file

@ -41,35 +41,6 @@ void Sphere3DOverlay::render(RenderArgs* args) {
transform.postScale(getDimensions());
batch->setModelTransform(transform);
DependencyManager::get<GeometryCache>()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid);
} else {
glDisable(GL_LIGHTING);
glm::vec3 position = getPosition();
glm::vec3 center = getCenter();
glm::vec3 dimensions = getDimensions();
glm::quat rotation = getRotation();
float glowLevel = getGlowLevel();
Glower* glower = NULL;
if (glowLevel > 0.0f) {
glower = new Glower(glowLevel);
}
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
DependencyManager::get<GeometryCache>()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid);
glPopMatrix();
glPopMatrix();
if (glower) {
delete glower;
}
}
}

View file

@ -30,9 +30,6 @@
#include "EntityActionFactoryInterface.h"
const quint64 DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND);
const quint64 MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD = 2 * USECS_PER_SECOND;
bool EntityItem::_sendPhysicsUpdates = true;
int EntityItem::_maxActionsDataSize = 800;
@ -346,6 +343,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
return 0;
}
args.entitiesPerPacket++;
// Header bytes
// object ID [16 bytes]
// ByteCountCoded(type code) [~1 byte]
@ -426,6 +425,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
dataAt += sizeof(lastEditedFromBuffer);
bytesRead += sizeof(lastEditedFromBuffer);
lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
if (lastEditedFromBufferAdjusted > now) {
lastEditedFromBufferAdjusted = now;
}
@ -498,6 +498,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
#endif
}
encodedUpdateDelta = updateDeltaCoder; // determine true length
dataAt += encodedUpdateDelta.size();
bytesRead += encodedUpdateDelta.size();
@ -676,6 +677,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
}
// Tracking for editing roundtrips here. We will tell our EntityTree that we just got incoming data about
// and entity that was edited at some time in the past. The tree will determine how it wants to track this
// information.
if (_element && _element->getTree()) {
_element->getTree()->trackIncomingEntityLastEdited(lastEditedFromBufferAdjusted, bytesRead);
}
return bytesRead;
}

View file

@ -26,15 +26,13 @@
#include "LogHandler.h"
const quint64 SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND);
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage),
_fbxService(NULL),
_simulation(NULL)
{
_rootElement = createNewElement();
resetClientEditStats();
}
EntityTree::~EntityTree() {
@ -61,6 +59,8 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
}
_entityToElementMap.clear();
Octree::eraseAllOctreeElements(createNewRoot);
resetClientEditStats();
}
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
@ -1123,3 +1123,29 @@ bool EntityTree::readFromMap(QVariantMap& map) {
return true;
}
void EntityTree::resetClientEditStats() {
_treeResetTime = usecTimestampNow();
_maxEditDelta = 0;
_totalEditDeltas = 0;
_totalTrackedEdits = 0;
}
void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead) {
// we don't want to track all edit deltas, just those edits that have happend
// since we connected to this domain. This will filter out all previously created
// content and only track new edits
if (lastEditedTime > _treeResetTime) {
quint64 now = usecTimestampNow();
quint64 sinceEdit = now - lastEditedTime;
_totalEditDeltas += sinceEdit;
_totalEditBytes += bytesRead;
_totalTrackedEdits++;
if (sinceEdit > _maxEditDelta) {
_maxEditDelta = sinceEdit;
}
}
}

View file

@ -185,6 +185,14 @@ public:
virtual quint64 getAverageCreateTime() const { return _totalCreates == 0 ? 0 : _totalCreateTime / _totalCreates; }
virtual quint64 getAverageLoggingTime() const { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; }
void trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead);
quint64 getAverageEditDeltas() const
{ return _totalTrackedEdits == 0 ? 0 : _totalEditDeltas / _totalTrackedEdits; }
quint64 getAverageEditBytes() const
{ return _totalTrackedEdits == 0 ? 0 : _totalEditBytes / _totalTrackedEdits; }
quint64 getMaxEditDelta() const { return _maxEditDelta; }
quint64 getTotalTrackedEdits() const { return _totalTrackedEdits; }
signals:
void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID);
@ -230,6 +238,14 @@ private:
quint64 _totalUpdateTime = 0;
quint64 _totalCreateTime = 0;
quint64 _totalLoggingTime = 0;
// these performance statistics are only used in the client
void resetClientEditStats();
int _totalTrackedEdits = 0;
quint64 _totalEditBytes = 0;
quint64 _totalEditDeltas = 0;
quint64 _maxEditDelta = 0;
quint64 _treeResetTime = 0;
};
#endif // hifi_EntityTree_h

View file

@ -702,6 +702,8 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
int bytesRead = 0;
uint16_t numberOfEntities = 0;
int expectedBytesPerEntity = EntityItem::expectedBytes();
args.elementsPerPacket++;
if (bytesLeftToRead >= (int)sizeof(numberOfEntities)) {
// read our entities in....

View file

@ -667,11 +667,6 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co
element = NULL;
}
delete[] octalCode; // cleanup memory
#ifdef HAS_AUDIT_CHILDREN
if (element) {
element->auditChildren("Octree::getOctreeElementAt()");
}
#endif // def HAS_AUDIT_CHILDREN
return element;
}
@ -680,11 +675,6 @@ OctreeElement* Octree::getOctreeEnclosingElementAt(float x, float y, float z, fl
OctreeElement* element = nodeForOctalCode(_rootElement, octalCode, NULL);
delete[] octalCode; // cleanup memory
#ifdef HAS_AUDIT_CHILDREN
if (element) {
element->auditChildren("Octree::getOctreeElementAt()");
}
#endif // def HAS_AUDIT_CHILDREN
return element;
}

View file

@ -195,6 +195,8 @@ public:
SharedNodePointer sourceNode;
bool wantImportProgress;
PacketVersion bitstreamVersion;
int elementsPerPacket = 0;
int entitiesPerPacket = 0;
ReadBitstreamToTreeParams(
bool includeColor = WANT_COLOR,

View file

@ -71,19 +71,9 @@ void OctreeElement::init(unsigned char * octalCode) {
_childrenExternal = false;
#ifdef BLENDED_UNION_CHILDREN
_children.external = NULL;
_singleChildrenCount++;
#endif
_childrenCount[0]++;
// default pointers to child nodes to NULL
#ifdef HAS_AUDIT_CHILDREN
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
_childrenArray[i] = NULL;
}
#endif // def HAS_AUDIT_CHILDREN
#ifdef SIMPLE_CHILD_ARRAY
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
_simpleChildArray[i] = NULL;
@ -218,9 +208,6 @@ void OctreeElement::deleteChildAtIndex(int childIndex) {
_voxelNodeLeafCount++;
}
}
#ifdef HAS_AUDIT_CHILDREN
auditChildren("deleteChildAtIndex()");
#endif // def HAS_AUDIT_CHILDREN
}
// does not delete the node!
@ -236,10 +223,6 @@ OctreeElement* OctreeElement::removeChildAtIndex(int childIndex) {
_voxelNodeLeafCount++;
}
}
#ifdef HAS_AUDIT_CHILDREN
auditChildren("removeChildAtIndex()");
#endif // def HAS_AUDIT_CHILDREN
return returnedChild;
}
@ -255,60 +238,11 @@ bool OctreeElement::isParentOf(OctreeElement* possibleChild) const {
return false;
}
#ifdef HAS_AUDIT_CHILDREN
void OctreeElement::auditChildren(const char* label) const {
bool auditFailed = false;
for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) {
OctreeElement* testChildNew = getChildAtIndex(childIndex);
OctreeElement* testChildOld = _childrenArray[childIndex];
if (testChildNew != testChildOld) {
auditFailed = true;
}
}
const bool alwaysReport = false; // set this to true to get additional debugging
if (alwaysReport || auditFailed) {
qDebug("%s... auditChildren() %s <<<<", label, (auditFailed ? "FAILED" : "PASSED"));
qDebug(" _childrenExternal=%s", debug::valueOf(_childrenExternal));
qDebug(" childCount=%d", getChildCount());
QDebug bitOutput = qDebug().nospace();
bitOutput << " _childBitmask=";
outputBits(_childBitmask, bitOutput);
for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) {
OctreeElement* testChildNew = getChildAtIndex(childIndex);
OctreeElement* testChildOld = _childrenArray[childIndex];
qCebug("child at index %d... testChildOld=%p testChildNew=%p %s",
childIndex, testChildOld, testChildNew ,
((testChildNew != testChildOld) ? " DOES NOT MATCH <<<< BAD <<<<" : " - OK ")
);
}
qDebug("%s... auditChildren() <<<< DONE <<<<", label);
}
}
#endif // def HAS_AUDIT_CHILDREN
quint64 OctreeElement::_getChildAtIndexTime = 0;
quint64 OctreeElement::_getChildAtIndexCalls = 0;
quint64 OctreeElement::_setChildAtIndexTime = 0;
quint64 OctreeElement::_setChildAtIndexCalls = 0;
#ifdef BLENDED_UNION_CHILDREN
quint64 OctreeElement::_singleChildrenCount = 0;
quint64 OctreeElement::_twoChildrenOffsetCount = 0;
quint64 OctreeElement::_twoChildrenExternalCount = 0;
quint64 OctreeElement::_threeChildrenOffsetCount = 0;
quint64 OctreeElement::_threeChildrenExternalCount = 0;
quint64 OctreeElement::_couldStoreFourChildrenInternally = 0;
quint64 OctreeElement::_couldNotStoreFourChildrenInternally = 0;
#endif
quint64 OctreeElement::_externalChildrenCount = 0;
quint64 OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@ -341,319 +275,8 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const {
} break;
}
#endif // def SIMPLE_EXTERNAL_CHILDREN
#ifdef BLENDED_UNION_CHILDREN
PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls);
OctreeElement* result = NULL;
int childCount = getChildCount();
#ifdef HAS_AUDIT_CHILDREN
const char* caseStr = NULL;
#endif
switch (childCount) {
case 0:
#ifdef HAS_AUDIT_CHILDREN
caseStr = "0 child case";
#endif
break;
case 1: {
#ifdef HAS_AUDIT_CHILDREN
caseStr = "1 child case";
#endif
int indexOne = getNthBit(_childBitmask, 1);
if (indexOne == childIndex) {
result = _children.single;
}
} break;
case 2: {
#ifdef HAS_AUDIT_CHILDREN
caseStr = "2 child case";
#endif
int indexOne = getNthBit(_childBitmask, 1);
int indexTwo = getNthBit(_childBitmask, 2);
if (_childrenExternal) {
//assert(_children.external);
if (indexOne == childIndex) {
result = _children.external[0];
} else if (indexTwo == childIndex) {
result = _children.external[1];
}
} else {
if (indexOne == childIndex) {
int32_t offset = _children.offsetsTwoChildren[0];
result = (OctreeElement*)((uint8_t*)this + offset);
} else if (indexTwo == childIndex) {
int32_t offset = _children.offsetsTwoChildren[1];
result = (OctreeElement*)((uint8_t*)this + offset);
}
}
} break;
case 3: {
#ifdef HAS_AUDIT_CHILDREN
caseStr = "3 child case";
#endif
int indexOne = getNthBit(_childBitmask, 1);
int indexTwo = getNthBit(_childBitmask, 2);
int indexThree = getNthBit(_childBitmask, 3);
if (_childrenExternal) {
//assert(_children.external);
if (indexOne == childIndex) {
result = _children.external[0];
} else if (indexTwo == childIndex) {
result = _children.external[1];
} else if (indexThree == childIndex) {
result = _children.external[2];
} else {
}
} else {
int64_t offsetOne, offsetTwo, offsetThree;
decodeThreeOffsets(offsetOne, offsetTwo, offsetThree);
if (indexOne == childIndex) {
result = (OctreeElement*)((uint8_t*)this + offsetOne);
} else if (indexTwo == childIndex) {
result = (OctreeElement*)((uint8_t*)this + offsetTwo);
} else if (indexThree == childIndex) {
result = (OctreeElement*)((uint8_t*)this + offsetThree);
}
}
} break;
default: {
#ifdef HAS_AUDIT_CHILDREN
caseStr = "default";
#endif
// if we have 4 or more, we know we're in external mode, so we just need to figure out which
// slot in our external array this child is.
if (oneAtBit(_childBitmask, childIndex)) {
childCount = getChildCount();
for (int ordinal = 1; ordinal <= childCount; ordinal++) {
int index = getNthBit(_childBitmask, ordinal);
if (index == childIndex) {
int externalIndex = ordinal-1;
if (externalIndex < childCount && externalIndex >= 0) {
result = _children.external[externalIndex];
} else {
qCDebug(octree, "getChildAtIndex() attempt to access external client out of "
"bounds externalIndex=%d <<<<<<<<<< WARNING!!!", externalIndex);
}
break;
}
}
}
} break;
}
#ifdef HAS_AUDIT_CHILDREN
if (result != _childrenArray[childIndex]) {
qCDebug(octree, "getChildAtIndex() case:%s result<%p> != _childrenArray[childIndex]<%p> <<<<<<<<<< WARNING!!!",
caseStr, result,_childrenArray[childIndex]);
}
#endif // def HAS_AUDIT_CHILDREN
return result;
#endif
}
#ifdef BLENDED_UNION_CHILDREN
void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* childTwo) {
int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this;
int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this;
const int64_t minOffset = std::numeric_limits<int32_t>::min();
const int64_t maxOffset = std::numeric_limits<int32_t>::max();
bool forceExternal = true;
if (!forceExternal && isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) {
// if previously external, then clean it up...
if (_childrenExternal) {
//assert(_children.external);
const int previousChildCount = 2;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
delete[] _children.external;
_children.external = NULL; // probably not needed!
_childrenExternal = false;
}
// encode in union
_children.offsetsTwoChildren[0] = offsetOne;
_children.offsetsTwoChildren[1] = offsetTwo;
_twoChildrenOffsetCount++;
} else {
// encode in array
// if not previously external, then allocate appropriately
if (!_childrenExternal) {
_childrenExternal = true;
const int newChildCount = 2;
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
_children.external = new OctreeElement*[newChildCount];
memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount);
}
_children.external[0] = childOne;
_children.external[1] = childTwo;
_twoChildrenExternalCount++;
}
}
void OctreeElement::retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*& childTwo) {
// If we previously had an external array, then get the
if (_childrenExternal) {
childOne = _children.external[0];
childTwo = _children.external[1];
delete[] _children.external;
_children.external = NULL; // probably not needed!
_childrenExternal = false;
_twoChildrenExternalCount--;
const int newChildCount = 2;
_externalChildrenMemoryUsage -= newChildCount * sizeof(OctreeElement*);
} else {
int64_t offsetOne = _children.offsetsTwoChildren[0];
int64_t offsetTwo = _children.offsetsTwoChildren[1];
childOne = (OctreeElement*)((uint8_t*)this + offsetOne);
childTwo = (OctreeElement*)((uint8_t*)this + offsetTwo);
_twoChildrenOffsetCount--;
}
}
void OctreeElement::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const {
const quint64 ENCODE_BITS = 21;
const quint64 ENCODE_MASK = 0xFFFFF;
const quint64 ENCODE_MASK_SIGN = 0x100000;
quint64 offsetEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK;
quint64 offsetEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK;
quint64 offsetEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK);
quint64 signEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK_SIGN;
quint64 signEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK_SIGN;
quint64 signEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK_SIGN);
bool oneNegative = signEncodedOne == ENCODE_MASK_SIGN;
bool twoNegative = signEncodedTwo == ENCODE_MASK_SIGN;
bool threeNegative = signEncodedThree == ENCODE_MASK_SIGN;
offsetOne = oneNegative ? -offsetEncodedOne : offsetEncodedOne;
offsetTwo = twoNegative ? -offsetEncodedTwo : offsetEncodedTwo;
offsetThree = threeNegative ? -offsetEncodedThree : offsetEncodedThree;
}
void OctreeElement::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) {
const quint64 ENCODE_BITS = 21;
const quint64 ENCODE_MASK = 0xFFFFF;
const quint64 ENCODE_MASK_SIGN = 0x100000;
quint64 offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree;
if (offsetOne < 0) {
offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN);
} else {
offsetEncodedOne = offsetOne & ENCODE_MASK;
}
offsetEncodedOne = offsetEncodedOne << (ENCODE_BITS * 2);
if (offsetTwo < 0) {
offsetEncodedTwo = ((-offsetTwo & ENCODE_MASK) | ENCODE_MASK_SIGN);
} else {
offsetEncodedTwo = offsetTwo & ENCODE_MASK;
}
offsetEncodedTwo = offsetEncodedTwo << ENCODE_BITS;
if (offsetThree < 0) {
offsetEncodedThree = ((-offsetThree & ENCODE_MASK) | ENCODE_MASK_SIGN);
} else {
offsetEncodedThree = offsetThree & ENCODE_MASK;
}
_children.offsetsThreeChildrenEncoded = offsetEncodedOne | offsetEncodedTwo | offsetEncodedThree;
}
void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree) {
int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this;
int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this;
int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this;
const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits<int16_t>::min();
const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits<int16_t>::max();
bool forceExternal = true;
if (!forceExternal &&
isBetween(offsetOne, maxOffset, minOffset) &&
isBetween(offsetTwo, maxOffset, minOffset) &&
isBetween(offsetThree, maxOffset, minOffset)) {
// if previously external, then clean it up...
if (_childrenExternal) {
delete[] _children.external;
_children.external = NULL; // probably not needed!
_childrenExternal = false;
const int previousChildCount = 3;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
}
// encode in union
encodeThreeOffsets(offsetOne, offsetTwo, offsetThree);
_threeChildrenOffsetCount++;
} else {
// encode in array
// if not previously external, then allocate appropriately
if (!_childrenExternal) {
_childrenExternal = true;
const int newChildCount = 3;
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
_children.external = new OctreeElement*[newChildCount];
memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount);
}
_children.external[0] = childOne;
_children.external[1] = childTwo;
_children.external[2] = childThree;
_threeChildrenExternalCount++;
}
}
void OctreeElement::retrieveThreeChildren(OctreeElement*& childOne, OctreeElement*& childTwo, OctreeElement*& childThree) {
// If we previously had an external array, then get the
if (_childrenExternal) {
childOne = _children.external[0];
childTwo = _children.external[1];
childThree = _children.external[2];
delete[] _children.external;
_children.external = NULL; // probably not needed!
_childrenExternal = false;
_threeChildrenExternalCount--;
_externalChildrenMemoryUsage -= 3 * sizeof(OctreeElement*);
} else {
int64_t offsetOne, offsetTwo, offsetThree;
decodeThreeOffsets(offsetOne, offsetTwo, offsetThree);
childOne = (OctreeElement*)((uint8_t*)this + offsetOne);
childTwo = (OctreeElement*)((uint8_t*)this + offsetTwo);
childThree = (OctreeElement*)((uint8_t*)this + offsetThree);
_threeChildrenOffsetCount--;
}
}
void OctreeElement::checkStoreFourChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree, OctreeElement* childFour) {
int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this;
int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this;
int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this;
int64_t offsetFour = (uint8_t*)childFour - (uint8_t*)this;
const int64_t minOffset = std::numeric_limits<int16_t>::min();
const int64_t maxOffset = std::numeric_limits<int16_t>::max();
bool forceExternal = true;
if (!forceExternal &&
isBetween(offsetOne, maxOffset, minOffset) &&
isBetween(offsetTwo, maxOffset, minOffset) &&
isBetween(offsetThree, maxOffset, minOffset) &&
isBetween(offsetFour, maxOffset, minOffset)
) {
_couldStoreFourChildrenInternally++;
} else {
_couldNotStoreFourChildrenInternally++;
}
}
#endif
void OctreeElement::deleteAllChildren() {
// first delete all the OctreeElement objects...
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
@ -667,52 +290,6 @@ void OctreeElement::deleteAllChildren() {
// if the children_t union represents _children.external we need to delete it here
delete[] _children.external;
}
#ifdef BLENDED_UNION_CHILDREN
// now, reset our internal state and ANY and all population data
int childCount = getChildCount();
switch (childCount) {
case 0: {
_singleChildrenCount--;
_childrenCount[0]--;
} break;
case 1: {
_singleChildrenCount--;
_childrenCount[1]--;
} break;
case 2: {
if (_childrenExternal) {
_twoChildrenExternalCount--;
} else {
_twoChildrenOffsetCount--;
}
_childrenCount[2]--;
} break;
case 3: {
if (_childrenExternal) {
_threeChildrenExternalCount--;
} else {
_threeChildrenOffsetCount--;
}
_childrenCount[3]--;
} break;
default: {
_externalChildrenCount--;
_childrenCount[childCount]--;
} break;
}
// If we had externally stored children, clean them too.
if (_childrenExternal && _children.external) {
delete[] _children.external;
}
_children.single = NULL;
#endif // BLENDED_UNION_CHILDREN
}
void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) {
@ -788,353 +365,6 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) {
}
#endif // def SIMPLE_EXTERNAL_CHILDREN
#ifdef BLENDED_UNION_CHILDREN
PerformanceWarning warn(false,"setChildAtIndex",false,&_setChildAtIndexTime,&_setChildAtIndexCalls);
// Here's how we store things...
// If we have 0 or 1 children, then we just store them in the _children.single;
// If we have 2 children,
// then if we can we store them as 32 bit signed offsets from our own this pointer,
// _children.offsetsTwoChildren[0]-[1]
// these are 32 bit offsets
unsigned char previousChildMask = _childBitmask;
int previousChildCount = getChildCount();
if (child) {
setAtBit(_childBitmask, childIndex);
} else {
clearAtBit(_childBitmask, childIndex);
}
int newChildCount = getChildCount();
// track our population data
if (previousChildCount != newChildCount) {
_childrenCount[previousChildCount]--;
_childrenCount[newChildCount]++;
}
// If we had 0 children and we still have 0 children, then there is nothing to do.
if (previousChildCount == 0 && newChildCount == 0) {
// nothing to do...
} else if ((previousChildCount == 0 || previousChildCount == 1) && newChildCount == 1) {
// If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child,
// then we can just store it in _children.single
_children.single = child;
} else if (previousChildCount == 1 && newChildCount == 0) {
// If we had 1 child, and we've removed our last child, then we can just store NULL in _children.single
_children.single = NULL;
} else if (previousChildCount == 1 && newChildCount == 2) {
// If we had 1 child, and we're adding a second child, then we need to determine
// if we can use offsets to store them
OctreeElement* childOne;
OctreeElement* childTwo;
if (getNthBit(previousChildMask, 1) < childIndex) {
childOne = _children.single;
childTwo = child;
} else {
childOne = child;
childTwo = _children.single;
}
_singleChildrenCount--;
storeTwoChildren(childOne, childTwo);
} else if (previousChildCount == 2 && newChildCount == 1) {
// If we had 2 children, and we're removing one, then we know we can go down to single mode
//assert(child == NULL); // this is the only logical case
int indexTwo = getNthBit(previousChildMask, 2);
bool keepChildOne = indexTwo == childIndex;
OctreeElement* childOne;
OctreeElement* childTwo;
retrieveTwoChildren(childOne, childTwo);
_singleChildrenCount++;
if (keepChildOne) {
_children.single = childOne;
} else {
_children.single = childTwo;
}
} else if (previousChildCount == 2 && newChildCount == 2) {
// If we had 2 children, and still have 2, then we know we are resetting one of our existing children
int indexOne = getNthBit(previousChildMask, 1);
bool replaceChildOne = indexOne == childIndex;
// Get the existing two children out of their encoding...
OctreeElement* childOne;
OctreeElement* childTwo;
retrieveTwoChildren(childOne, childTwo);
if (replaceChildOne) {
childOne = child;
} else {
childTwo = child;
}
storeTwoChildren(childOne, childTwo);
} else if (previousChildCount == 2 && newChildCount == 3) {
// If we had 2 children, and now have 3, then we know we are going to an external case...
// First, decode the children...
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
// Get the existing two children out of their encoding...
retrieveTwoChildren(childOne, childTwo);
// determine order of the existing children
int indexOne = getNthBit(previousChildMask, 1);
int indexTwo = getNthBit(previousChildMask, 2);
if (childIndex < indexOne) {
childThree = childTwo;
childTwo = childOne;
childOne = child;
} else if (childIndex < indexTwo) {
childThree = childTwo;
childTwo = child;
} else {
childThree = child;
}
storeThreeChildren(childOne, childTwo, childThree);
} else if (previousChildCount == 3 && newChildCount == 2) {
// If we had 3 children, and now have 2, then we know we are going from an external case to a potential internal case
// We need to determine which children we had, and which one we got rid of...
int indexOne = getNthBit(previousChildMask, 1);
int indexTwo = getNthBit(previousChildMask, 2);
bool removeChildOne = indexOne == childIndex;
bool removeChildTwo = indexTwo == childIndex;
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
// Get the existing two children out of their encoding...
retrieveThreeChildren(childOne, childTwo, childThree);
if (removeChildOne) {
childOne = childTwo;
childTwo = childThree;
} else if (removeChildTwo) {
childTwo = childThree;
} else {
// removing child three, nothing to do.
}
storeTwoChildren(childOne, childTwo);
} else if (previousChildCount == 3 && newChildCount == 3) {
// If we had 3 children, and now have 3, then we need to determine which item we're replacing...
// We need to determine which children we had, and which one we got rid of...
int indexOne = getNthBit(previousChildMask, 1);
int indexTwo = getNthBit(previousChildMask, 2);
bool replaceChildOne = indexOne == childIndex;
bool replaceChildTwo = indexTwo == childIndex;
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
// Get the existing two children out of their encoding...
retrieveThreeChildren(childOne, childTwo, childThree);
if (replaceChildOne) {
childOne = child;
} else if (replaceChildTwo) {
childTwo = child;
} else {
childThree = child;
}
storeThreeChildren(childOne, childTwo, childThree);
} else if (previousChildCount == 3 && newChildCount == 4) {
// If we had 3 children, and now have 4, then we know we are going to an external case...
// First, decode the children...
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
OctreeElement* childFour;
// Get the existing two children out of their encoding...
retrieveThreeChildren(childOne, childTwo, childThree);
// determine order of the existing children
int indexOne = getNthBit(previousChildMask, 1);
int indexTwo = getNthBit(previousChildMask, 2);
int indexThree = getNthBit(previousChildMask, 3);
if (childIndex < indexOne) {
childFour = childThree;
childThree = childTwo;
childTwo = childOne;
childOne = child;
} else if (childIndex < indexTwo) {
childFour = childThree;
childThree = childTwo;
childTwo = child;
} else if (childIndex < indexThree) {
childFour = childThree;
childThree = child;
} else {
childFour = child;
}
// now, allocate the external...
_childrenExternal = true;
const int newChildCount = 4;
_children.external = new OctreeElement*[newChildCount];
memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
_children.external[0] = childOne;
_children.external[1] = childTwo;
_children.external[2] = childThree;
_children.external[3] = childFour;
_externalChildrenCount++;
} else if (previousChildCount == 4 && newChildCount == 3) {
// If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case
//assert(_children.external && _childrenExternal && previousChildCount == 4);
// We need to determine which children we had, and which one we got rid of...
int indexOne = getNthBit(previousChildMask, 1);
int indexTwo = getNthBit(previousChildMask, 2);
int indexThree = getNthBit(previousChildMask, 3);
bool removeChildOne = indexOne == childIndex;
bool removeChildTwo = indexTwo == childIndex;
bool removeChildThree = indexThree == childIndex;
OctreeElement* childOne = _children.external[0];
OctreeElement* childTwo = _children.external[1];
OctreeElement* childThree = _children.external[2];
OctreeElement* childFour = _children.external[3];
if (removeChildOne) {
childOne = childTwo;
childTwo = childThree;
childThree = childFour;
} else if (removeChildTwo) {
childTwo = childThree;
childThree = childFour;
} else if (removeChildThree) {
childThree = childFour;
} else {
// removing child four, nothing to do.
}
// clean up the external children...
_childrenExternal = false;
delete[] _children.external;
_children.external = NULL;
_externalChildrenCount--;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
storeThreeChildren(childOne, childTwo, childThree);
} else if (previousChildCount == newChildCount) {
//assert(_children.external && _childrenExternal && previousChildCount >= 4);
//assert(previousChildCount == newChildCount);
// 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one
// that needs to be replaced and replace it.
for (int ordinal = 1; ordinal <= 8; ordinal++) {
int index = getNthBit(previousChildMask, ordinal);
if (index == childIndex) {
// this is our child to be replaced
int nthChild = ordinal-1;
_children.external[nthChild] = child;
break;
}
}
} else if (previousChildCount < newChildCount) {
// Growing case... previous must be 4 or greater
//assert(_children.external && _childrenExternal && previousChildCount >= 4);
//assert(previousChildCount == newChildCount-1);
// 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert
// this child pointer into our external list
OctreeElement** newExternalList = new OctreeElement*[newChildCount];
memset(newExternalList, 0, sizeof(OctreeElement*) * newChildCount);
int copiedCount = 0;
for (int ordinal = 1; ordinal <= newChildCount; ordinal++) {
int index = getNthBit(previousChildMask, ordinal);
if (index != -1 && index < childIndex) {
newExternalList[ordinal - 1] = _children.external[ordinal - 1];
copiedCount++;
} else {
// insert our new child here...
newExternalList[ordinal - 1] = child;
// if we didn't copy all of our previous children, then we need to
if (copiedCount < previousChildCount) {
// our child needs to be inserted before this index, and everything else pushed out...
for (int oldOrdinal = ordinal; oldOrdinal <= previousChildCount; oldOrdinal++) {
newExternalList[oldOrdinal] = _children.external[oldOrdinal - 1];
}
}
break;
}
}
delete[] _children.external;
_children.external = newExternalList;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
} else if (previousChildCount > newChildCount) {
//assert(_children.external && _childrenExternal && previousChildCount >= 4);
//assert(previousChildCount == newChildCount+1);
// 4 or more children, one item being removed, we know we're stored externally, we just figure out which
// item to remove from our external list
OctreeElement** newExternalList = new OctreeElement*[newChildCount];
for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) {
int index = getNthBit(previousChildMask, ordinal);
//assert(index != -1);
if (index < childIndex) {
newExternalList[ordinal - 1] = _children.external[ordinal - 1];
} else {
// our child needs to be removed from here, and everything else pulled in...
for (int moveOrdinal = ordinal; moveOrdinal <= newChildCount; moveOrdinal++) {
newExternalList[moveOrdinal - 1] = _children.external[moveOrdinal];
}
break;
}
}
delete[] _children.external;
_children.external = newExternalList;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
} else {
//assert(false);
qCDebug(octree, "THIS SHOULD NOT HAPPEN previousChildCount == %d && newChildCount == %d",previousChildCount, newChildCount);
}
// check to see if we could store these 4 children locally
if (getChildCount() == 4 && _childrenExternal && _children.external) {
checkStoreFourChildren(_children.external[0], _children.external[1], _children.external[2], _children.external[3]);
}
#ifdef HAS_AUDIT_CHILDREN
_childrenArray[childIndex] = child;
auditChildren("setChildAtIndex()");
#endif // def HAS_AUDIT_CHILDREN
#endif
}

View file

@ -12,7 +12,6 @@
#ifndef hifi_OctreeElement_h
#define hifi_OctreeElement_h
//#define HAS_AUDIT_CHILDREN
//#define SIMPLE_CHILD_ARRAY
#define SIMPLE_EXTERNAL_CHILDREN
@ -204,25 +203,9 @@ public:
static quint64 getSetChildAtIndexTime() { return _setChildAtIndexTime; }
static quint64 getSetChildAtIndexCalls() { return _setChildAtIndexCalls; }
#ifdef BLENDED_UNION_CHILDREN
static quint64 getSingleChildrenCount() { return _singleChildrenCount; }
static quint64 getTwoChildrenOffsetCount() { return _twoChildrenOffsetCount; }
static quint64 getTwoChildrenExternalCount() { return _twoChildrenExternalCount; }
static quint64 getThreeChildrenOffsetCount() { return _threeChildrenOffsetCount; }
static quint64 getThreeChildrenExternalCount() { return _threeChildrenExternalCount; }
static quint64 getCouldStoreFourChildrenInternally() { return _couldStoreFourChildrenInternally; }
static quint64 getCouldNotStoreFourChildrenInternally() { return _couldNotStoreFourChildrenInternally; }
#endif
static quint64 getExternalChildrenCount() { return _externalChildrenCount; }
static quint64 getChildrenCount(int childCount) { return _childrenCount[childCount]; }
#ifdef BLENDED_UNION_CHILDREN
#ifdef HAS_AUDIT_CHILDREN
void auditChildren(const char* label) const;
#endif // def HAS_AUDIT_CHILDREN
#endif // def BLENDED_UNION_CHILDREN
enum ChildIndex {
CHILD_BOTTOM_RIGHT_NEAR = 0,
CHILD_BOTTOM_RIGHT_FAR = 1,
@ -261,15 +244,6 @@ protected:
void deleteAllChildren();
void setChildAtIndex(int childIndex, OctreeElement* child);
#ifdef BLENDED_UNION_CHILDREN
void storeTwoChildren(OctreeElement* childOne, OctreeElement* childTwo);
void retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*& childTwo);
void storeThreeChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree);
void retrieveThreeChildren(OctreeElement*& childOne, OctreeElement*& childTwo, OctreeElement*& childThree);
void decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const;
void encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree);
void checkStoreFourChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree, OctreeElement* childFour);
#endif
void calculateAACube();
void notifyDeleteHooks();
void notifyUpdateHooks();
@ -296,19 +270,6 @@ protected:
} _children;
#endif
#ifdef BLENDED_UNION_CHILDREN
union children_t {
OctreeElement* single;
int32_t offsetsTwoChildren[2];
quint64 offsetsThreeChildrenEncoded;
OctreeElement** external;
} _children;
#ifdef HAS_AUDIT_CHILDREN
OctreeElement* _childrenArray[8]; /// Only used when HAS_AUDIT_CHILDREN is enabled to help debug children encoding
#endif // def HAS_AUDIT_CHILDREN
#endif //def BLENDED_UNION_CHILDREN
uint16_t _sourceUUIDKey; /// Client only, stores node id of voxel server that sent his voxel, 2 bytes
// Support for _sourceUUID, we use these static member variables to track the UUIDs that are
@ -345,15 +306,6 @@ protected:
static quint64 _setChildAtIndexTime;
static quint64 _setChildAtIndexCalls;
#ifdef BLENDED_UNION_CHILDREN
static quint64 _singleChildrenCount;
static quint64 _twoChildrenOffsetCount;
static quint64 _twoChildrenExternalCount;
static quint64 _threeChildrenOffsetCount;
static quint64 _threeChildrenExternalCount;
static quint64 _couldStoreFourChildrenInternally;
static quint64 _couldNotStoreFourChildrenInternally;
#endif
static quint64 _externalChildrenCount;
static quint64 _childrenCount[NUMBER_OF_CHILDREN + 1];
};

View file

@ -12,9 +12,11 @@
#include <glm/glm.hpp>
#include <stdint.h>
#include <SharedUtil.h>
#include <NumericalConstants.h>
#include <PerfStat.h>
#include <RenderArgs.h>
#include <SharedUtil.h>
#include "OctreeLogging.h"
#include "OctreeRenderer.h"
@ -101,6 +103,15 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
sequence, flightTime, packetLength, dataBytes);
}
_packetsInLastWindow++;
int elementsPerPacket = 0;
int entitiesPerPacket = 0;
quint64 totalWaitingForLock = 0;
quint64 totalUncompress = 0;
quint64 totalReadBitsteam = 0;
int subsection = 1;
while (dataBytes > 0) {
if (packetIsCompressed) {
@ -120,7 +131,12 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
sourceUUID, sourceNode, false, packetVersion);
quint64 startLock = usecTimestampNow();
// FIXME STUTTER - there may be an opportunity to bump this lock outside of the
// loop to reduce the amount of locking/unlocking we're doing
_tree->lockForWrite();
quint64 startUncompress = usecTimestampNow();
OctreePacketData packetData(packetIsCompressed);
packetData.loadFinalizedContent(dataAt, sectionLength);
if (extraDebugging) {
@ -134,17 +150,56 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
if (extraDebugging) {
qCDebug(octree) << "OctreeRenderer::processDatagram() ******* START _tree->readBitstreamToTree()...";
}
quint64 startReadBitsteam = usecTimestampNow();
_tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
quint64 endReadBitsteam = usecTimestampNow();
if (extraDebugging) {
qCDebug(octree) << "OctreeRenderer::processDatagram() ******* END _tree->readBitstreamToTree()...";
}
_tree->unlock();
dataBytes -= sectionLength;
dataAt += sectionLength;
elementsPerPacket += args.elementsPerPacket;
entitiesPerPacket += args.entitiesPerPacket;
_elementsInLastWindow += args.elementsPerPacket;
_entitiesInLastWindow += args.entitiesPerPacket;
totalWaitingForLock += (startUncompress - startLock);
totalUncompress += (startReadBitsteam - startUncompress);
totalReadBitsteam += (endReadBitsteam - startReadBitsteam);
}
subsection++;
}
_elementsPerPacket.updateAverage(elementsPerPacket);
_entitiesPerPacket.updateAverage(entitiesPerPacket);
_waitLockPerPacket.updateAverage(totalWaitingForLock);
_uncompressPerPacket.updateAverage(totalUncompress);
_readBitstreamPerPacket.updateAverage(totalReadBitsteam);
quint64 now = usecTimestampNow();
if (_lastWindowAt == 0) {
_lastWindowAt = now;
}
quint64 sinceLastWindow = now - _lastWindowAt;
if (sinceLastWindow > USECS_PER_SECOND) {
float packetsPerSecondInWindow = (float)_packetsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND);
float elementsPerSecondInWindow = (float)_elementsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND);
float entitiesPerSecondInWindow = (float)_entitiesInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND);
_packetsPerSecond.updateAverage(packetsPerSecondInWindow);
_elementsPerSecond.updateAverage(elementsPerSecondInWindow);
_entitiesPerSecond.updateAverage(entitiesPerSecondInWindow);
_lastWindowAt = now;
_packetsInLastWindow = 0;
_elementsInLastWindow = 0;
_entitiesInLastWindow = 0;
}
subsection++;
}
}

View file

@ -61,12 +61,40 @@ public:
/// clears the tree
virtual void clear();
float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); }
float getAverageEntitiesPerPacket() const { return _entitiesPerPacket.getAverage(); }
float getAveragePacketsPerSecond() const { return _packetsPerSecond.getAverage(); }
float getAverageElementsPerSecond() const { return _elementsPerSecond.getAverage(); }
float getAverageEntitiesPerSecond() const { return _entitiesPerSecond.getAverage(); }
float getAverageWaitLockPerPacket() const { return _waitLockPerPacket.getAverage(); }
float getAverageUncompressPerPacket() const { return _uncompressPerPacket.getAverage(); }
float getAverageReadBitstreamPerPacket() const { return _readBitstreamPerPacket.getAverage(); }
protected:
virtual Octree* createTree() = 0;
Octree* _tree;
bool _managedTree;
ViewFrustum* _viewFrustum;
SimpleMovingAverage _elementsPerPacket;
SimpleMovingAverage _entitiesPerPacket;
SimpleMovingAverage _packetsPerSecond;
SimpleMovingAverage _elementsPerSecond;
SimpleMovingAverage _entitiesPerSecond;
SimpleMovingAverage _waitLockPerPacket;
SimpleMovingAverage _uncompressPerPacket;
SimpleMovingAverage _readBitstreamPerPacket;
quint64 _lastWindowAt = 0;
int _packetsInLastWindow = 0;
int _elementsInLastWindow = 0;
int _entitiesInLastWindow = 0;
};
#endif // hifi_OctreeRenderer_h

View file

@ -146,7 +146,6 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& renderDetails = renderContext->args->_details;
RenderArgs* args = renderContext->args;
gpu::Batch batch;

View file

@ -59,7 +59,6 @@ void render::cullItems(const SceneContextPointer& sceneContext, const RenderCont
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
auto renderDetails = renderContext->args->_details._item;
@ -101,7 +100,6 @@ void render::cullItems(const SceneContextPointer& sceneContext, const RenderCont
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) {
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(_filter);
auto& renderDetails = renderContext->args->_details;
outItems.clear();
outItems.reserve(items.size());