Merge branch 'protocol' of https://github.com/birarda/hifi into protocol

This commit is contained in:
Atlante45 2015-07-08 18:26:36 -07:00
commit e2718d5c94
128 changed files with 3739 additions and 4879 deletions

View file

@ -133,6 +133,9 @@ if (APPLE)
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX10.9.sdk)
endif ()
# Hide automoc folders (for IDEs)
set(AUTOGEN_TARGETS_FOLDER "hidden/generated")
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc automatically when needed.

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

@ -86,4 +86,7 @@ elseif(NOT ANDROID)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARY} ${${EXTERNAL_NAME_UPPER}_LIBRARY_EXTRAS} CACHE TYPE INTERNAL)
endif()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")

View file

@ -38,6 +38,9 @@ else ()
)
endif ()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -12,6 +12,9 @@ if (WIN32)
LOG_DOWNLOAD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -12,6 +12,9 @@ ExternalProject_Add(
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -16,6 +16,9 @@ ExternalProject_Add(
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -12,6 +12,9 @@ ExternalProject_Add(
LOG_DOWNLOAD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include ${SOURCE_DIR}/implement CACHE TYPE INTERNAL)

View file

@ -15,6 +15,9 @@ ExternalProject_Add(
LOG_DOWNLOAD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNAL)

View file

@ -12,6 +12,8 @@ ExternalProject_Add(
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)

View file

@ -36,6 +36,9 @@ ExternalProject_Add(
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
if (CMAKE_GENERATOR STREQUAL Xcode)

View file

@ -38,6 +38,9 @@ else ()
)
endif ()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (APPLE)

View file

@ -16,6 +16,9 @@ ExternalProject_Add(
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -53,6 +53,9 @@ else ()
)
endif ()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -16,6 +16,9 @@ ExternalProject_Add(
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)

View file

@ -0,0 +1,139 @@
#
# SetupHifiTestCase.cmake
#
# 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
#
# Sets up a hifi testcase using QtTest.
# Can be called with arguments; like setup_hifi_project, the arguments correspond to qt modules, so call it
# via setup_hifi_testcase(Script Network Qml) to build the testcase with Qt5Script, Qt5Network, and Qt5Qml linked,
# for example.
# One special quirk of this is that because we are creating multiple testcase targets (instead of just one),
# any dependencies and other setup that the testcase has must be declared in a macro, and setup_hifi_testcase()
# must be called *after* this declaration, not before it.
# Here's a full example:
# tests/my-foo-test/CMakeLists.txt:
#
# # Declare testcase dependencies
# macro (setup_hifi_testcase)
# bunch
# of
# custom
# dependencies...
#
# link_hifi_libraries(shared networking etc)
#
# copy_dlls_beside_windows_executable()
# endmacro()
#
# setup_hifi_testcase(Network etc)
#
# Additionally, all .cpp files in the src dir (eg tests/my-foo-test/src) must:
# - Contain exactly one test class (any supporting code must be either external or inline in a .hpp file)
# - Be built against QtTestLib (test class should be a QObject, with test methods defined as private slots)
# - Contain a QTEST_MAIN declaration at the end of the file (or at least be a standalone executable)
#
# All other testing infrastructure is generated automatically.
#
macro(SETUP_HIFI_TESTCASE)
if (NOT DEFINED TEST_PROJ_NAME)
message(SEND_ERROR "Missing TEST_PROJ_NAME (setup_hifi_testcase was called incorrectly?)")
elseif (NOT COMMAND SETUP_TESTCASE_DEPENDENCIES)
message(SEND_ERROR "Missing testcase dependencies declaration (SETUP_TESTCASE_DEPENDENCIES)")
elseif (DEFINED ${TEST_PROJ_NAME}_BUILT)
message(WARNING "testcase \"" ${TEST_PROJ_NAME} "\" was already built")
else ()
set(${TEST_PROJ_NAME}_BUILT 1)
file(GLOB TEST_PROJ_SRC_FILES src/*)
file(GLOB TEST_PROJ_SRC_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/*)
foreach (DIR ${TEST_PROJ_SRC_SUBDIRS})
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/${DIR}")
file(GLOB DIR_CONTENTS "src/${DIR}/*")
set(TEST_PROJ_SRC_FILES ${TEST_PROJ_SRC_FILES} "${DIR_CONTENTS}")
endif()
endforeach()
# Find test classes to build into test executables.
# Warn about any .cpp files that are *not* test classes (*Test[s].cpp), since those files will not be used.
foreach (SRC_FILE ${TEST_PROJ_SRC_FILES})
string(REGEX MATCH ".+Tests?\\.cpp$" TEST_CPP_FILE ${SRC_FILE})
string(REGEX MATCH ".+\\.cpp$" NON_TEST_CPP_FILE ${SRC_FILE})
if (TEST_CPP_FILE)
list(APPEND TEST_CASE_FILES ${TEST_CPP_FILE})
elseif (NON_TEST_CPP_FILE)
message(WARNING "ignoring .cpp file (not a test class -- this will not be linked or compiled!): " ${NON_TEST_CPP_FILE})
endif ()
endforeach ()
if (TEST_CASE_FILES)
set(TEST_PROJ_TARGETS "")
# Add each test class executable (duplicates functionality in SetupHifiProject.cmake)
# The resulting targets will be buried inside of hidden/test-executables so that they don't clutter up
# the project view in Xcode, Visual Studio, etc.
foreach (TEST_FILE ${TEST_CASE_FILES})
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
set(TARGET_NAME ${TEST_PROJ_NAME}-${TEST_NAME})
project(${TARGET_NAME})
# grab the implemenation and header files
set(TARGET_SRCS ${TEST_FILE}) # only one source / .cpp file (the test class)
add_executable(${TARGET_NAME} ${TEST_FILE})
add_test(${TARGET_NAME}-test ${TARGET_NAME})
set_target_properties(${TARGET_NAME} PROPERTIES
EXCLUDE_FROM_DEFAULT_BUILD TRUE
EXCLUDE_FROM_ALL TRUE)
list (APPEND ${TEST_PROJ_NAME}_TARGETS ${TARGET_NAME})
#list (APPEND ALL_TEST_TARGETS ${TARGET_NAME})
set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN})
list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core Test)
# find these Qt modules and link them to our own target
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
endforeach()
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "hidden/test-executables")
# handle testcase-specific dependencies (this a macro that should be defined in the cmakelists.txt file in each tests subdir)
SETUP_TESTCASE_DEPENDENCIES ()
endforeach ()
set(TEST_TARGET ${TEST_PROJ_NAME}-tests)
# Add a dummy target so that the project files are visible.
# This target will also build + run the other test targets using ctest when built.
add_custom_target(${TEST_TARGET}
COMMAND ctest .
SOURCES ${TEST_PROJ_SRC_FILES} # display source files under the testcase target
DEPENDS ${${TEST_PROJ_NAME}_TARGETS})
set_target_properties(${TEST_TARGET} PROPERTIES
EXCLUDE_FROM_DEFAULT_BUILD TRUE
EXCLUDE_FROM_ALL TRUE)
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER "Tests")
list (APPEND ALL_TEST_TARGETS ${TEST_TARGET})
set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE)
else ()
message(WARNING "No testcases in " ${TEST_PROJ_NAME})
endif ()
endif ()
endmacro(SETUP_HIFI_TESTCASE)

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
test = function(name, func) {
var test = function(name, func) {
print("Running test: " + name);
var unitTest = new UnitTest(name, func);

1316
examples/utilities/tools/cookies.js Executable file → Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@
Script.include("cookies.js");
var panel = new Panel(10, 800);
var panel = new Panel(10, 100);
panel.newSlider("Num Feed Opaques", 0, 1000,
function(value) { },
@ -66,6 +66,12 @@ panel.newSlider("Max Drawn Overlay3Ds", -1, 100,
function(value) { return (value); }
);
panel.newCheckbox("Display status",
function(value) { Scene.setEngineDisplayItemStatus(value); },
function() { return Scene.doEngineDisplayItemStatus(); },
function(value) { return (value); }
);
var tickTackPeriod = 500;
function updateCounters() {

View file

@ -3527,6 +3527,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems();
renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus();
renderArgs->_shouldRender = LODManager::shouldRender;
renderContext.args = renderArgs;

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

@ -750,7 +750,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
const int text_y = -nameDynamicRect.height() / 2;
// Compute background position/size
static const float SLIGHTLY_BEHIND = -0.05f;
static const float SLIGHTLY_IN_FRONT = 0.1f;
const int border = 0.1f * nameDynamicRect.height();
const int left = text_x - border;
const int bottom = text_y - border;
@ -765,16 +765,16 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
// Compute display name transform
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
// Render background slightly behind to avoid z-fighting
auto backgroundTransform = textTransform;
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
batch.setModelTransform(backgroundTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
batch.setModelTransform(textTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor);
// Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
// Render text slightly in front to avoid z-fighting
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
batch.setModelTransform(textTransform);
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
}

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

@ -21,11 +21,14 @@
#include "Application.h"
#include "../octree/OctreePacketProcessor.h"
#include "ui/OctreeStatsDialog.h"
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 +53,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
_localElements = AddStatItem("Local Elements");
_localElementsMemory = AddStatItem("Elements Memory");
_sendingMode = AddStatItem("Sending Mode");
_processedPackets = AddStatItem("Entity 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 +130,36 @@ 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);
}
const int FLOATING_POINT_PRECISION = 3;
_lastRefresh = now;
// Update labels
QLabel* label;
@ -203,9 +244,110 @@ 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 averageElementsPerSecond = entities->getAverageElementsPerSecond();
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket();
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION);
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION);
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION);
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION);
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
label = _labels[_processedPackets];
const OctreePacketProcessor& entitiesPacketProcessor = Application::getInstance()->getOctreePacketProcessor();
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION);
QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION);
QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION);
statsValue.str("");
statsValue <<
"Network IN: " << qPrintable(incomingPPSString) << " PPS / " <<
"Queue OUT: " << qPrintable(processedPPSString) << " PPS / " <<
"Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS";
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, 'f', FLOATING_POINT_PRECISION);
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

@ -13,6 +13,7 @@
#include "Text3DOverlay.h"
#include <DeferredLightingEffect.h>
#include <RenderDeferredTask.h>
#include <TextRenderer3D.h>
@ -114,6 +115,7 @@ void Text3DOverlay::render(RenderArgs* args) {
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, false, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
// Same font properties as textSize()

View file

@ -167,6 +167,25 @@ namespace render {
}
}
void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item::Status::Getters& statusGetters) {
statusGetters.push_back([entity] () -> render::Item::Status::Value {
quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote();
const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND);
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
// Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green)
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : render::Item::Status::Value::RED));
});
statusGetters.push_back([entity] () -> render::Item::Status::Value {
quint64 delta = usecTimestampNow() - entity->getLastBroadcast();
const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND);
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
// Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green)
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : render::Item::Status::Value::CYAN));
});
}
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) {
_myMetaItem = scene->allocateID();
@ -177,7 +196,9 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
pendingChanges.resetItem(_myMetaItem, renderPayload);
if (_model) {
return _model->addToScene(scene, pendingChanges);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(this, statusGetters);
return _model->addToScene(scene, pendingChanges, statusGetters);
}
return true;
@ -214,7 +235,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
render::PendingChanges pendingChanges;
if (_model->needsFixupInScene()) {
_model->removeFromScene(scene, pendingChanges);
_model->addToScene(scene, pendingChanges);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(this, statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters);
}
scene->enqueuePendingChanges(pendingChanges);

View file

@ -36,10 +36,6 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
glm::vec3 dimensions = getDimensions();
Transform transformToTopLeft = getTransformToCenter();
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
// Render background
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
@ -48,15 +44,22 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
Transform transformToTopLeft = getTransformToCenter();
if (getFaceCamera()) {
//rotate about vertical to face the camera
glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
transformToTopLeft.setRotation(orientation);
}
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
batch.setModelTransform(transformToTopLeft);
//rotate about vertical to face the camera
if (getFaceCamera()) {
transformToTopLeft.postRotate(args->_viewFrustum->getOrientation());
batch.setModelTransform(transformToTopLeft);
}
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false, false, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
float scale = _lineHeight / _textRenderer->getFontSize();

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::Value packetType) const {
@ -249,7 +249,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
if (getIsClient()) {
// if our Node isn't allowed to create entities in this domain, don't try.
auto nodeList = DependencyManager::get<NodeList>();
if (!nodeList->getThisNodeCanRez()) {
if (nodeList && !nodeList->getThisNodeCanRez()) {
return NULL;
}
}
@ -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

@ -159,6 +159,12 @@ void Batch::setProjectionTransform(const Mat4& proj) {
_params.push_back(cacheData(sizeof(Mat4), &proj));
}
void Batch::setViewportTransform(const Vec4i& viewport) {
ADD_COMMAND(setViewportTransform);
_params.push_back(cacheData(sizeof(Vec4i), &viewport));
}
void Batch::setPipeline(const PipelinePointer& pipeline) {
ADD_COMMAND(setPipeline);

View file

@ -105,6 +105,7 @@ public:
void setModelTransform(const Transform& model);
void setViewTransform(const Transform& view);
void setProjectionTransform(const Mat4& proj);
void setViewportTransform(const Vec4i& viewport); // Viewport is xy = low left corner in the framebuffer, zw = width height of the viewport
// Pipeline Stage
void setPipeline(const PipelinePointer& pipeline);
@ -154,6 +155,7 @@ public:
void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void _glUniform3fv(GLint location, GLsizei count, const GLfloat* value);
void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value);
void _glUniform4iv(GLint location, GLsizei count, const GLint* value);
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void _glEnableVertexAttribArray(GLint location);
@ -177,6 +179,7 @@ public:
COMMAND_setModelTransform,
COMMAND_setViewTransform,
COMMAND_setProjectionTransform,
COMMAND_setViewportTransform,
COMMAND_setPipeline,
COMMAND_setStateBlendFactor,
@ -217,6 +220,7 @@ public:
COMMAND_glUniform3f,
COMMAND_glUniform3fv,
COMMAND_glUniform4fv,
COMMAND_glUniform4iv,
COMMAND_glUniformMatrix4fv,
COMMAND_glEnableVertexAttribArray,

View file

@ -18,11 +18,13 @@
<@elif GLPROFILE == MAC_GL @>
<@def GPU_FEATURE_PROFILE GPU_LEGACY@>
<@def GPU_TRANSFORM_PROFILE GPU_LEGACY@>
<@def VERSION_HEADER #version 120@>
<@def VERSION_HEADER #version 120
#extension GL_EXT_gpu_shader4 : enable@>
<@else@>
<@def GPU_FEATURE_PROFILE GPU_LEGACY@>
<@def GPU_TRANSFORM_PROFILE GPU_LEGACY@>
<@def VERSION_HEADER #version 120@>
<@def VERSION_HEADER #version 120
#extension GL_EXT_gpu_shader4 : enable@>
<@endif@>
<@endif@>

View file

@ -38,6 +38,7 @@ typedef uint32 Offset;
typedef glm::mat4 Mat4;
typedef glm::mat3 Mat3;
typedef glm::vec4 Vec4;
typedef glm::ivec4 Vec4i;
typedef glm::vec3 Vec3;
typedef glm::vec2 Vec2;
typedef glm::ivec2 Vec2i;

View file

@ -29,6 +29,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_setModelTransform),
(&::gpu::GLBackend::do_setViewTransform),
(&::gpu::GLBackend::do_setProjectionTransform),
(&::gpu::GLBackend::do_setViewportTransform),
(&::gpu::GLBackend::do_setPipeline),
(&::gpu::GLBackend::do_setStateBlendFactor),
@ -67,6 +68,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_glUniform3f),
(&::gpu::GLBackend::do_glUniform3fv),
(&::gpu::GLBackend::do_glUniform4fv),
(&::gpu::GLBackend::do_glUniform4iv),
(&::gpu::GLBackend::do_glUniformMatrix4fv),
(&::gpu::GLBackend::do_glEnableVertexAttribArray),
@ -190,6 +192,18 @@ void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) {
}
void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) {
updateInput();
updateTransform();
updatePipeline();
GLint numInstances = batch._params[paramOffset + 4]._uint;
Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint;
GLenum mode = _primitiveToGLmode[primitiveType];
uint32 numVertices = batch._params[paramOffset + 2]._uint;
uint32 startVertex = batch._params[paramOffset + 1]._uint;
uint32 startInstance = batch._params[paramOffset + 0]._uint;
glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances);
(void) CHECK_GL_ERROR();
}
@ -592,6 +606,31 @@ void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) {
(void) CHECK_GL_ERROR();
}
void Batch::_glUniform4iv(GLint location, GLsizei count, const GLint* value) {
ADD_COMMAND_GL(glUniform4iv);
const int VEC4_SIZE = 4 * sizeof(int);
_params.push_back(cacheData(count * VEC4_SIZE, value));
_params.push_back(count);
_params.push_back(location);
DO_IT_NOW(_glUniform4iv, 3);
}
void GLBackend::do_glUniform4iv(Batch& batch, uint32 paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
return;
}
updatePipeline();
glUniform4iv(
batch._params[paramOffset + 2]._int,
batch._params[paramOffset + 1]._uint,
(const GLint*)batch.editData(batch._params[paramOffset + 0]._uint));
(void) CHECK_GL_ERROR();
}
void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
ADD_COMMAND_GL(glUniformMatrix4fv);

View file

@ -96,7 +96,9 @@ public:
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
#else
GLuint _transformObject_model = -1;
GLuint _transformCamera_viewInverse = -1;
GLuint _transformCamera_viewport = -1;
#endif
GLShader();
@ -267,7 +269,8 @@ protected:
void do_setModelTransform(Batch& batch, uint32 paramOffset);
void do_setViewTransform(Batch& batch, uint32 paramOffset);
void do_setProjectionTransform(Batch& batch, uint32 paramOffset);
void do_setViewportTransform(Batch& batch, uint32 paramOffset);
void initTransform();
void killTransform();
// Synchronize the state cache of this Backend with the actual real state of the GL Context
@ -281,9 +284,11 @@ protected:
Transform _model;
Transform _view;
Mat4 _projection;
Vec4i _viewport;
bool _invalidModel;
bool _invalidView;
bool _invalidProj;
bool _invalidViewport;
GLenum _lastMode;
@ -296,6 +301,7 @@ protected:
_invalidModel(true),
_invalidView(true),
_invalidProj(false),
_invalidViewport(false),
_lastMode(GL_TEXTURE) {}
} _transform;
@ -329,7 +335,9 @@ protected:
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
#else
GLint _program_transformObject_model = -1;
GLint _program_transformCamera_viewInverse = -1;
GLint _program_transformCamera_viewport = -1;
#endif
State::Data _stateCache;
@ -390,6 +398,7 @@ protected:
void do_glUniform3f(Batch& batch, uint32 paramOffset);
void do_glUniform3fv(Batch& batch, uint32 paramOffset);
void do_glUniform4fv(Batch& batch, uint32 paramOffset);
void do_glUniform4iv(Batch& batch, uint32 paramOffset);
void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset);
void do_glEnableVertexAttribArray(Batch& batch, uint32 paramOffset);

View file

@ -73,7 +73,9 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) {
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
#else
_pipeline._program_transformObject_model = -1;
_pipeline._program_transformCamera_viewInverse = -1;
_pipeline._program_transformCamera_viewport = -1;
#endif
_pipeline._state = nullptr;
@ -91,7 +93,9 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) {
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
#else
_pipeline._program_transformObject_model = pipelineObject->_program->_transformObject_model;
_pipeline._program_transformCamera_viewInverse = pipelineObject->_program->_transformCamera_viewInverse;
_pipeline._program_transformCamera_viewport = pipelineObject->_program->_transformCamera_viewport;
#endif
}
@ -143,10 +147,20 @@ void GLBackend::updatePipeline() {
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
#else
// If shader program needs the model we need to provide it
if (_pipeline._program_transformObject_model >= 0) {
glUniformMatrix4fv(_pipeline._program_transformObject_model, 1, false, (const GLfloat*) &_transform._transformObject._model);
}
// If shader program needs the inverseView we need to provide it
if (_pipeline._program_transformCamera_viewInverse >= 0) {
glUniformMatrix4fv(_pipeline._program_transformCamera_viewInverse, 1, false, (const GLfloat*) &_transform._transformCamera._viewInverse);
}
// If shader program needs the viewport we need to provide it
if (_pipeline._program_transformCamera_viewport >= 0) {
glUniform4fv(_pipeline._program_transformCamera_viewport, 1, (const GLfloat*) &_transform._transformCamera._viewport);
}
#endif
}

View file

@ -111,10 +111,20 @@ void makeBindings(GLBackend::GLShader* shader) {
shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
}
#else
loc = glGetUniformLocation(glprogram, "transformObject_model");
if (loc >= 0) {
shader->_transformObject_model = loc;
}
loc = glGetUniformLocation(glprogram, "transformCamera_viewInverse");
if (loc >= 0) {
shader->_transformCamera_viewInverse = loc;
}
loc = glGetUniformLocation(glprogram, "transformCamera_viewport");
if (loc >= 0) {
shader->_transformCamera_viewport = loc;
}
#endif
}

View file

@ -589,7 +589,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
void GLBackend::do_setStateDepthBias(Vec2 bias) {
if ( (bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) {
if ((bias.x != 0.f) || (bias.y != 0.f)) {
if ((bias.x != 0.0f) || (bias.y != 0.0f)) {
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_POLYGON_OFFSET_POINT);

View file

@ -31,6 +31,12 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
_transform._invalidProj = true;
}
void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
_transform._invalidViewport = true;
}
void GLBackend::initTransform() {
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
glGenBuffers(1, &_transform._transformObjectBuffer);
@ -57,10 +63,13 @@ void GLBackend::killTransform() {
}
void GLBackend::syncTransformStateCache() {
_transform._invalidViewport = true;
_transform._invalidProj = true;
_transform._invalidView = true;
_transform._invalidModel = true;
glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport);
GLint currentMode;
glGetIntegerv(GL_MATRIX_MODE, &currentMode);
_transform._lastMode = currentMode;
@ -78,6 +87,13 @@ void GLBackend::updateTransform() {
GLint originalMatrixMode;
glGetIntegerv(GL_MATRIX_MODE, &originalMatrixMode);
// Check all the dirty flags and update the state accordingly
if (_transform._invalidViewport) {
_transform._transformCamera._viewport = glm::vec4(_transform._viewport);
// Where we assign the GL viewport
glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w);
}
if (_transform._invalidProj) {
_transform._transformCamera._projection = _transform._projection;
_transform._transformCamera._projectionInverse = glm::inverse(_transform._projection);
@ -100,7 +116,7 @@ void GLBackend::updateTransform() {
}
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
if (_transform._invalidView || _transform._invalidProj) {
if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) {
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
@ -162,7 +178,8 @@ void GLBackend::updateTransform() {
#endif
// Flags are clean
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = false;
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false;
glMatrixMode(originalMatrixMode);
}

View file

@ -87,10 +87,18 @@ TransformCamera getTransformCamera() {
}
uniform mat4 transformCamera_viewInverse;
uniform vec4 transformCamera_viewport;
<@endif@>
<@endfunc@>
<@func transformCameraViewport(cameraTransform, viewport)@>
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
<$viewport$> = <$cameraTransform$>._viewport;
<@else@>
<$viewport$> = transformCamera_viewport;
<@endif@>
<@endfunc@>
<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>

View file

@ -9,10 +9,17 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <NumericalConstants.h>
#include "NodeList.h"
#include "ReceivedPacketProcessor.h"
#include "SharedUtil.h"
ReceivedPacketProcessor::ReceivedPacketProcessor() {
_lastWindowAt = usecTimestampNow();
}
void ReceivedPacketProcessor::terminating() {
_hasPackets.wakeAll();
}
@ -27,6 +34,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
lock();
_packets.push_back(std::move(networkPacket));
_nodePacketCounts[sendingNode->getUUID()]++;
_lastWindowIncomingPackets++;
unlock();
// Make sure to wake our actual processing thread because we now have packets for it to process.
@ -34,6 +42,24 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
}
bool ReceivedPacketProcessor::process() {
quint64 now = usecTimestampNow();
quint64 sinceLastWindow = now - _lastWindowAt;
if (sinceLastWindow > USECS_PER_SECOND) {
lock();
float secondsSinceLastWindow = sinceLastWindow / USECS_PER_SECOND;
float incomingPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow;
_incomingPPS.updateAverage(incomingPacketsPerSecondInWindow);
float processedPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow;
_processedPPS.updateAverage(processedPacketsPerSecondInWindow);
_lastWindowAt = now;
_lastWindowIncomingPackets = 0;
_lastWindowProcessedPackets = 0;
unlock();
}
if (_packets.size() == 0) {
_waitingOnPacketsMutex.lock();

View file

@ -20,7 +20,7 @@
class ReceivedPacketProcessor : public GenericThread {
Q_OBJECT
public:
ReceivedPacketProcessor() { }
ReceivedPacketProcessor();
/// Add packet from network receive thread to the processing queue.
void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
@ -46,6 +46,9 @@ public:
/// How many received packets waiting are to be processed
int packetsToProcessCount() const { return _packets.size(); }
float getIncomingPPS() const { return _incomingPPS.getAverage(); }
float getProcessedPPS() const { return _processedPPS.getAverage(); }
virtual void terminating();
public slots:
@ -78,6 +81,12 @@ protected:
QWaitCondition _hasPackets;
QMutex _waitingOnPacketsMutex;
quint64 _lastWindowAt = 0;
int _lastWindowIncomingPackets = 0;
int _lastWindowProcessedPackets = 0;
SimpleMovingAverage _incomingPPS;
SimpleMovingAverage _processedPPS;
};
#endif // hifi_ReceivedPacketProcessor_h

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

@ -50,37 +50,44 @@
static const std::string glowIntensityShaderHandle = "glowIntensity";
gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config) {
auto it = _simplePrograms.find(config);
if (it != _simplePrograms.end()) {
return it.value();
}
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
if (config.isCulled()) {
state->setCullMode(gpu::State::CULL_BACK);
} else {
state->setCullMode(gpu::State::CULL_NONE);
}
state->setDepthTest(true, true, gpu::LESS_EQUAL);
if (config.hasDepthBias()) {
state->setDepthBias(1.0f);
state->setDepthBiasSlopeScale(1.0f);
}
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader;
gpu::PipelinePointer pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
_simplePrograms.insert(config, pipeline);
return pipeline;
}
void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert)));
auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag)));
auto PSEmissive = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_emisive_frag)));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS));
gpu::ShaderPointer programEmissive = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive));
_simpleShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS));
_emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive));
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::Shader::makeProgram(*programEmissive, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
gpu::StatePointer stateCullNone = gpu::StatePointer(new gpu::State());
stateCullNone->setCullMode(gpu::State::CULL_NONE);
stateCullNone->setDepthTest(true, true, gpu::LESS_EQUAL);
stateCullNone->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
_simpleProgramCullNone = gpu::PipelinePointer(gpu::Pipeline::create(program, stateCullNone));
_simpleProgramEmissive = gpu::PipelinePointer(gpu::Pipeline::create(programEmissive, state));
_simpleProgramEmissiveCullNone = gpu::PipelinePointer(gpu::Pipeline::create(programEmissive, stateCullNone));
gpu::Shader::makeProgram(*_simpleShader, slotBindings);
gpu::Shader::makeProgram(*_emissiveShader, slotBindings);
_viewState = viewState;
loadLightProgram(directional_light_frag, false, _directionalLight, _directionalLightLocations);
@ -117,21 +124,12 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET));
}
void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool emmisive) {
if (emmisive) {
if (culled) {
batch.setPipeline(_simpleProgramEmissive);
} else {
batch.setPipeline(_simpleProgramEmissiveCullNone);
}
} else {
if (culled) {
batch.setPipeline(_simpleProgram);
} else {
batch.setPipeline(_simpleProgramCullNone);
}
}
if (!textured) {
void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled,
bool emmisive, bool depthBias) {
SimpleProgramKey config{textured, culled, emmisive, depthBias};
batch.setPipeline(getPipeline(config));
if (!config.isTextured()) {
// If it is not textured, bind white texture and keep using textured pipeline
batch.setUniformTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
}

View file

@ -24,6 +24,7 @@
class AbstractViewStateInterface;
class RenderArgs;
class SimpleProgramKey;
/// Handles deferred lighting for the bits that require it (voxels...)
class DeferredLightingEffect : public Dependency {
@ -34,7 +35,8 @@ public:
void init(AbstractViewStateInterface* viewState);
/// Sets up the state necessary to render static untextured geometry with the simple program.
void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, bool emmisive = false);
void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true,
bool emmisive = false, bool depthBias = false);
//// Renders a solid sphere with the simple program.
void renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color);
@ -95,11 +97,11 @@ private:
};
static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations);
gpu::PipelinePointer getPipeline(SimpleProgramKey config);
gpu::PipelinePointer _simpleProgram;
gpu::PipelinePointer _simpleProgramCullNone;
gpu::PipelinePointer _simpleProgramEmissive;
gpu::PipelinePointer _simpleProgramEmissiveCullNone;
gpu::ShaderPointer _simpleShader;
gpu::ShaderPointer _emissiveShader;
QHash<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
ProgramObject _directionalSkyboxLight;
LightLocations _directionalSkyboxLightLocations;
@ -160,4 +162,53 @@ private:
model::SkyboxPointer _skybox;
};
class SimpleProgramKey {
public:
enum FlagBit {
IS_TEXTURED_FLAG = 0,
IS_CULLED_FLAG,
IS_EMISSIVE_FLAG,
HAS_DEPTH_BIAS_FLAG,
NUM_FLAGS,
};
enum Flag {
IS_TEXTURED = (1 << IS_TEXTURED_FLAG),
IS_CULLED = (1 << IS_CULLED_FLAG),
IS_EMISSIVE = (1 << IS_EMISSIVE_FLAG),
HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG),
};
typedef unsigned short Flags;
bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); }
bool isTextured() const { return isFlag(IS_TEXTURED); }
bool isCulled() const { return isFlag(IS_CULLED); }
bool isEmissive() const { return isFlag(IS_EMISSIVE); }
bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); }
Flags _flags = 0;
short _spare = 0;
int getRaw() const { return *reinterpret_cast<const int*>(this); }
SimpleProgramKey(bool textured = false, bool culled = true,
bool emissive = false, bool depthBias = false) {
_flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) |
(emissive ? IS_EMISSIVE : 0) | (depthBias ? HAS_DEPTH_BIAS : 0);
}
SimpleProgramKey(int bitmask) : _flags(bitmask) {}
};
inline uint qHash(const SimpleProgramKey& key, uint seed) {
return qHash(key.getRaw(), seed);
}
inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) {
return a.getRaw() == b.getRaw();
}
#endif // hifi_DeferredLightingEffect_h

View file

@ -910,6 +910,38 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
return somethingAdded;
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) {
if (!_meshGroupsKnown && isLoadedWithTextures()) {
segregateMeshGroups();
}
bool somethingAdded = false;
foreach (auto renderItem, _transparentRenderItems) {
auto item = scene->allocateID();
auto renderData = MeshPartPayload::Pointer(renderItem);
auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData));
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(item, renderPayload);
_renderItems.insert(item, renderPayload);
somethingAdded = true;
}
foreach (auto renderItem, _opaqueRenderItems) {
auto item = scene->allocateID();
auto renderData = MeshPartPayload::Pointer(renderItem);
auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData));
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(item, renderPayload);
_renderItems.insert(item, renderPayload);
somethingAdded = true;
}
_readyWhenAdded = readyToAddToScene();
return somethingAdded;
}
void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
foreach (auto item, _renderItems.keys()) {
pendingChanges.removeItem(item);

View file

@ -118,6 +118,7 @@ public:
bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); }
bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoadedWithTextures(); }
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters);
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
/// Sets the URL of the model to render.

View file

@ -17,6 +17,8 @@
#include "RenderArgs.h"
#include "TextureCache.h"
#include "render/DrawStatus.h"
#include <PerfStat.h>
#include "overlay3D_vert.h"
@ -49,6 +51,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
)));
_jobs.push_back(Job(new CullItems::JobModel("CullOpaque", _jobs.back().getOutput())));
_jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput())));
auto& renderedOpaques = _jobs.back().getOutput();
_jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput())));
_jobs.push_back(Job(new DrawLight::JobModel("DrawLight")));
_jobs.push_back(Job(new ResetGLState::JobModel()));
@ -65,6 +68,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_jobs.push_back(Job(new CullItems::JobModel("CullTransparent", _jobs.back().getOutput())));
_jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false))));
_jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput())));
_jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques)));
_jobs.back().setEnabled(false);
_drawStatusJobIndex = _jobs.size() - 1;
_jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D")));
_jobs.push_back(Job(new ResetGLState::JobModel()));
}
@ -85,6 +93,9 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
return;
}
// Make sure we turn the displayItemStatus on/off
setDrawItemStatus(renderContext->_drawItemStatus);
renderContext->args->_context->syncCache();
for (auto job : _jobs) {
@ -135,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

@ -70,6 +70,11 @@ public:
render::Jobs _jobs;
int _drawStatusJobIndex = -1;
void setDrawItemStatus(bool draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw); } }
bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } }
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
};

View file

@ -0,0 +1,174 @@
//
// DrawStatus.cpp
// render/src/render
//
// Created by Niraj Venkat on 6/29/15.
// 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
//
#include <algorithm>
#include <assert.h>
#include "DrawStatus.h"
#include <PerfStat.h>
#include "gpu/GPULogging.h"
#include "gpu/Batch.h"
#include "gpu/Context.h"
#include "ViewFrustum.h"
#include "RenderArgs.h"
#include "drawItemBounds_vert.h"
#include "drawItemBounds_frag.h"
#include "drawItemStatus_vert.h"
#include "drawItemStatus_frag.h"
using namespace render;
const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() {
if (!_drawItemBoundsPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag)));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
_drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos");
_drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim");
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(true, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
// Good to go add the brand new pipeline
_drawItemBoundsPipeline.reset(gpu::Pipeline::create(program, state));
}
return _drawItemBoundsPipeline;
}
const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() {
if (!_drawItemStatusPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag)));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
_drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos");
_drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim");
_drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus");
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(false, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
// Good to go add the brand new pipeline
_drawItemStatusPipeline.reset(gpu::Pipeline::create(program, state));
}
return _drawItemStatusPipeline;
}
void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
RenderArgs* args = renderContext->args;
auto& scene = sceneContext->_scene;
// FIrst thing, we collect the bound and the status for all the items we want to render
int nbItems = 0;
{
if (!_itemBounds) {
_itemBounds.reset(new gpu::Buffer());
}
if (!_itemStatus) {
_itemStatus.reset(new gpu::Buffer());
}
_itemBounds->resize((inItems.size() * sizeof(AABox)));
_itemStatus->resize((inItems.size() * sizeof(glm::vec4)));
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
for (auto& item : inItems) {
if (!item.bounds.isInvalid()) {
if (!item.bounds.isNull()) {
(*itemAABox) = item.bounds;
} else {
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
}
auto& itemScene = scene->getItem(item.id);
(*itemStatus) = itemScene.getStatusPackedValues();
nbItems++;
itemAABox++;
itemStatus++;
}
}
}
if (nbItems == 0) {
return;
}
// Allright, something to render let's do it
gpu::Batch batch;
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setModelTransform(Transform());
// bind the one gpu::Pipeline we need
batch.setPipeline(getDrawItemBoundsPipeline());
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
const unsigned int VEC3_ADRESS_OFFSET = 3;
for (int i = 0; i < nbItems; i++) {
batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const GLfloat*) (itemAABox + i));
batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
batch.draw(gpu::LINES, 24, 0);
}
batch.setPipeline(getDrawItemStatusPipeline());
for (int i = 0; i < nbItems; i++) {
batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const GLfloat*) (itemAABox + i));
batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const GLint*) (itemStatus + i));
batch.draw(gpu::TRIANGLES, 24, 0);
}
// Before rendering the batch make sure we re in sync with gl state
args->_context->syncCache();
renderContext->args->_context->syncCache();
args->_context->render((batch));
}

View file

@ -0,0 +1,43 @@
//
// DrawStatus.h
// render/src/render
//
// Created by Niraj Venkat on 6/29/15.
// 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
//
#ifndef hifi_render_DrawStatus_h
#define hifi_render_DrawStatus_h
#include "DrawTask.h"
#include "gpu/Batch.h"
namespace render {
class DrawStatus {
int _drawItemBoundPosLoc = -1;
int _drawItemBoundDimLoc = -1;
int _drawItemStatusPosLoc = -1;
int _drawItemStatusDimLoc = -1;
int _drawItemStatusValueLoc = -1;
gpu::Stream::FormatPointer _drawItemFormat;
gpu::PipelinePointer _drawItemBoundsPipeline;
gpu::PipelinePointer _drawItemStatusPipeline;
gpu::BufferPointer _itemBounds;
gpu::BufferPointer _itemStatus;
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
typedef Job::ModelI<DrawStatus, ItemIDsBounds> JobModel;
const gpu::PipelinePointer& getDrawItemBoundsPipeline();
const gpu::PipelinePointer& getDrawItemStatusPipeline();
};
}
#endif // hifi_render_DrawStatus_h

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());
@ -128,9 +126,10 @@ struct ItemBound {
float _nearDepth = 0.0f;
float _farDepth = 0.0f;
ItemID _id = 0;
AABox _bounds;
ItemBound() {}
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {}
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
};
struct FrontToBackSort {
@ -167,7 +166,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende
auto bound = itemDetails.bounds; // item.getBound();
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id));
itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id, bound));
}
// sort against Z
@ -181,7 +180,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende
// FInally once sorted result to a list of itemID
for (auto& itemBound : itemBounds) {
outItems.emplace_back(itemBound._id);
outItems.emplace_back(ItemIDAndBounds(itemBound._id, itemBound._bounds));
}
}

View file

@ -77,6 +77,9 @@ public:
Job(const Job& other) : _concept(other._concept) {}
~Job();
bool isEnabled() const { return _concept->isEnabled(); }
void setEnabled(bool isEnabled) { _concept->setEnabled(isEnabled); }
const std::string& getName() const { return _concept->getName(); }
const Varying getInput() const { return _concept->getInput(); }
const Varying getOutput() const { return _concept->getOutput(); }
@ -92,6 +95,7 @@ public:
class Concept {
std::string _name;
bool _isEnabled = true;
public:
Concept() : _name() {}
Concept(const std::string& name) : _name(name) {}
@ -99,7 +103,10 @@ public:
void setName(const std::string& name) { _name = name; }
const std::string& getName() const { return _name; }
bool isEnabled() const { return _isEnabled; }
void setEnabled(bool isEnabled) { _isEnabled = isEnabled; }
virtual const Varying getInput() const { return Varying(); }
virtual const Varying getOutput() const { return Varying(); }
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0;
@ -119,7 +126,11 @@ public:
Model(Data data): _data(data) {}
Model(Data data, const std::string& name): Concept(name), _data(data) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRun(_data, sceneContext, renderContext); }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
if (isEnabled()) {
jobRun(_data, sceneContext, renderContext);
}
}
};
template <class T, class I> class ModelI : public Concept {
@ -135,7 +146,11 @@ public:
ModelI(const std::string& name, const Varying& input): Concept(name), _input(input) {}
ModelI(const std::string& name, Data data): Concept(name), _data(data) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRunI(_data, sceneContext, renderContext, _input.get<I>()); }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
if (isEnabled()) {
jobRunI(_data, sceneContext, renderContext, _input.get<I>());
}
}
};
template <class T, class O> class ModelO : public Concept {
@ -155,7 +170,9 @@ public:
ModelO(const std::string& name, Data data): Concept(name), _data(data), _output(Output()) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
jobRunO(_data, sceneContext, renderContext, _output.edit<O>());
if (isEnabled()) {
jobRunO(_data, sceneContext, renderContext, _output.edit<O>());
}
}
};
@ -177,7 +194,11 @@ public:
void setInput(const Varying& input) { _input = input; }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRunIO(_data, sceneContext, renderContext, _input.get<I>(), _output.edit<O>()); }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
if (isEnabled()) {
jobRunIO(_data, sceneContext, renderContext, _input.get<I>(), _output.edit<O>());
}
}
};
std::shared_ptr<Concept> _concept;

View file

@ -49,6 +49,8 @@ public:
int _numDrawnOverlay3DItems = 0;
int _maxDrawnOverlay3DItems = -1;
bool _drawItemStatus = false;
RenderContext() {}
};
typedef std::shared_ptr<RenderContext> RenderContextPointer;

View file

@ -10,6 +10,8 @@
//
#include "Scene.h"
#include <numeric>
using namespace render;
void ItemBucketMap::insert(const ItemID& id, const ItemKey& key) {
@ -53,6 +55,50 @@ void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
(*this)[ItemFilter::Builder::transparentShape().withLayered()];
}
const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value();
const float Item::Status::Value::RED = 0.0f;
const float Item::Status::Value::YELLOW = 60.0f;
const float Item::Status::Value::GREEN = 120.0f;
const float Item::Status::Value::CYAN = 180.0f;
const float Item::Status::Value::BLUE = 240.0f;
const float Item::Status::Value::MAGENTA = 300.0f;
void Item::Status::Value::setScale(float scale) {
_scale = (std::numeric_limits<unsigned short>::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f));
}
void Item::Status::Value::setColor(float hue) {
// Convert the HUe from range [0, 360] to signed normalized value
const float HUE_MAX = 360.0f;
_color = (std::numeric_limits<unsigned short>::max() - 1) * 0.5f * (1.0f + std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
}
void Item::Status::getPackedValues(glm::ivec4& values) const {
for (unsigned int i = 0; i < values.length(); i++) {
if (i < _values.size()) {
values[i] = _values[i]().getPackedData();
} else {
values[i] = Value::INVALID.getPackedData();
}
}
}
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
if (!_status) {
_status.reset(new Status());
}
_status->addGetter(getter);
}
void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) {
if (!_status) {
_status.reset(new Status());
}
for (auto& g : getters) {
_status->addGetter(g);
}
}
void Item::resetPayload(const PayloadPointer& payload) {
if (!payload) {
@ -63,6 +109,15 @@ void Item::resetPayload(const PayloadPointer& payload) {
}
}
glm::ivec4 Item::getStatusPackedValues() const {
glm::ivec4 values(Status::Value::INVALID.getPackedData());
auto& status = getStatus();
if (status) {
status->getPackedValues(values);
};
return values;
}
void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
_resetItems.push_back(id);
_resetPayloads.push_back(payload);

View file

@ -196,12 +196,50 @@ public:
// Bound is the AABBox fully containing this item
typedef AABox Bound;
// Stats records the life history and performances of this item while performing at rendering and updating.
// Status records the life history and performances of this item while performing at rendering and updating.
// This is Used for monitoring and dynamically adjust the quality
class Stats {
class Status {
public:
int _firstFrame;
// Status::Value class is the data used to represent the transient information of a status as a square icon
// The "icon" is a square displayed in the 3D scene over the render::Item AABB center.
// It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue
class Value {
unsigned short _scale = 0xFFFF;
unsigned short _color = 0xFFFF;
public:
const static Value INVALID; // Invalid value meanss the status won't show
Value() {}
Value(float scale, float hue) { setScale(scale); setColor(hue); }
// It can be scaled in the range [0, 1]
void setScale(float scale);
// the color hue in the range [0, 360] representing the color wheel hue
void setColor(float hue);
// Standard color Hue
static const float RED; // 0.0f;
static const float YELLOW; // 60.0f;
static const float GREEN; // 120.0f;
static const float CYAN; // 180.0f;
static const float BLUE; // 240.0f;
static const float MAGENTA; // 300.0f;
// Retreive the Value data tightely packed as an int
int getPackedData() const { return *((const int*) this); }
};
typedef std::function<Value()> Getter;
typedef std::vector<Getter> Getters;
Getters _values;
void addGetter(const Getter& getter) { _values.push_back(getter); }
void getPackedValues(glm::ivec4& values) const;
};
typedef std::shared_ptr<Status> StatusPointer;
// Update Functor
class UpdateFunctorInterface {
@ -222,7 +260,15 @@ public:
virtual const model::MaterialKey getMaterialKey() const = 0;
~PayloadInterface() {}
// Status interface is local to the base class
const StatusPointer& getStatus() const { return _status; }
void addStatusGetter(const Status::Getter& getter);
void addStatusGetters(const Status::Getters& getters);
protected:
StatusPointer _status;
friend class Item;
virtual void update(const UpdateFunctorPointer& functor) = 0;
};
@ -253,6 +299,10 @@ public:
// Shape Type Interface
const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); }
// Access the status
const StatusPointer& getStatus() const { return _payload->getStatus(); }
glm::ivec4 getStatusPackedValues() const;
protected:
PayloadPointer _payload;
ItemKey _key;

View file

@ -0,0 +1,19 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// drawItemBounds.frag
// fragment shader
//
// Created by Sam Gateau on 6/29/15.
// 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
//
varying vec4 varColor;
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

View file

@ -0,0 +1,57 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// drawItemBounds.slv
// vertex shader
//
// Created by Sam Gateau on 6/29/2015.
// 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
//
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
uniform vec3 inBoundPos;
uniform vec3 inBoundDim;
void main(void) {
const vec4 UNIT_BOX[8] = vec4[8](
vec4(0.0, 0.0, 0.0, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(1.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0),
vec4(1.0, 0.0, 1.0, 1.0),
vec4(0.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0)
);
const int UNIT_BOX_LINE_INDICES[24] = int[24](
0, 1,
1, 3,
3, 2,
2, 0,
4, 5,
5, 7,
7, 6,
6, 4,
2, 6,
3, 7,
0, 4,
1, 5
);
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
pos.xyz = inBoundPos + inBoundDim * pos.xyz;
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, pos, gl_Position)$>
// varTexcoord = (pos.xy + 1) * 0.5;
}

View file

@ -0,0 +1,19 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// drawItemStatus.frag
// fragment shader
//
// Created by Sam Gateau on 6/30/15.
// 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
//
varying vec4 varColor;
void main(void) {
gl_FragColor = varColor;
}

View file

@ -0,0 +1,101 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// drawItemStatus.slv
// vertex shader
//
// Created by Sam Gateau on 6/30/2015.
// 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
//
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
varying vec4 varColor;
uniform vec3 inBoundPos;
uniform vec3 inBoundDim;
uniform ivec4 inStatus;
vec3 paintRainbow(float nv) {
float v = nv * 5.f;
if (v < 0.f) {
return vec3(0.f, 0.f, 0.f);
} else if (v < 1.f) {
return vec3(1.f, v, 0.f);
} else if (v < 2.f) {
return vec3(1.f - (v-1.f), 1.f, 0.f);
} else if (v < 3.f) {
return vec3(0.f, 1.f, (v-2.f));
} else if (v < 4.f) {
return vec3(0.f, 1.f - (v-3.f), 1.f );
} else if (v < 5.f) {
return vec3((v-4.f), 0.f, 1.f );
} else {
return vec3(1.f, 1.f, 1.f);
}
}
vec2 unpackStatus(int v) {
return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0),
clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0));
}
void main(void) {
const vec2 ICON_PIXEL_SIZE = vec2(10, 10);
const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2);
const int NUM_VERTICES = 6;
const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES](
vec4(-1.0, -1.0, 0.0, 1.0),
vec4(1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4(1.0, -1.0, 0.0, 1.0),
vec4(1.0, 1.0, 0.0, 1.0)
);
// anchor point in clip space
vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0);
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$>
// Which icon are we dealing with ?
int iconNum = gl_VertexID / NUM_VERTICES;
// if invalid, just kill
if (inStatus[iconNum] == 0xFFFFFFFF) {
gl_Position = anchorPoint;
varColor = vec4(1.0);
return;
}
// unpack to get x and y satus
vec2 iconStatus = unpackStatus(inStatus[iconNum]);
// Use the status for showing a color
varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0);
// Also changes the size of the notification
vec2 iconScale = ICON_PIXEL_SIZE;
iconScale = max(vec2(1, 1), (iconScale * iconStatus.x));
//Offset icon to the right based on the iconNum
vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0);
// Final position in pixel space
int twoTriID = gl_VertexID - iconNum * NUM_VERTICES;
vec4 pos = UNIT_QUAD[twoTriID];
vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale;
vec4 viewport;
<$transformCameraViewport(cam, viewport)$>;
vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w);
gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0));
}

View file

@ -106,6 +106,10 @@ public:
Q_INVOKABLE int getEngineMaxDrawnTransparentItems() { return _maxDrawnTransparentItems; }
Q_INVOKABLE void setEngineMaxDrawnOverlay3DItems(int count) { _maxDrawnOverlay3DItems = count; }
Q_INVOKABLE int getEngineMaxDrawnOverlay3DItems() { return _maxDrawnOverlay3DItems; }
Q_INVOKABLE void setEngineDisplayItemStatus(bool display) { _drawItemStatus = display; }
Q_INVOKABLE bool doEngineDisplayItemStatus() { return _drawItemStatus; }
signals:
void shouldRenderAvatarsChanged(bool shouldRenderAvatars);
void shouldRenderEntitiesChanged(bool shouldRenderEntities);
@ -136,6 +140,8 @@ protected:
int _maxDrawnTransparentItems = -1;
int _maxDrawnOverlay3DItems = -1;
bool _drawItemStatus = false;
};
#endif // hifi_SceneScriptingInterface_h

View file

@ -1,9 +1,37 @@
# Turn on testing (so that add_test works)
enable_testing()
# add the test directories
file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
list(REMOVE_ITEM TEST_SUBDIRS "CMakeFiles")
foreach(DIR ${TEST_SUBDIRS})
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}")
add_subdirectory(${DIR})
set_target_properties("${DIR}-tests" PROPERTIES FOLDER "Tests")
endif()
endforeach()
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}")
set(TEST_PROJ_NAME ${DIR})
add_subdirectory(${DIR})
endif()
endforeach()
file(GLOB SHARED_TEST_HEADER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp")
add_custom_target("test-extensions"
SOURCES "${SHARED_TEST_HEADER_FILES}")
list(APPEND ALL_TEST_TARGETS "test-extensions")
set_target_properties("test-extensions" PROPERTIES FOLDER "Tests")
message(STATUS "ALL_TEST_TARGETS = ${ALL_TEST_TARGETS}")
# Create the all-tests build target.
# The dependency list (ALL_TEST_TARGETS) is generated from setup_hifi_testcase invocations in the CMakeLists.txt
# files in the test subdirs. Note: since variables normally do *not* persist into parent scope, we use a hack:
#
# list(APPEND ALL_TEST_TARGETS ${targets_to_add...}) # appends to a local list var (copied from parent scope)
# set (ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE) # copies this back to parent scope
#
add_custom_target("all-tests"
COMMAND ctest .
DEPENDS "${ALL_TEST_TARGETS}")
set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets")
set_target_properties("all-tests" PROPERTIES
EXCLUDE_FROM_DEFAULT_BUILD TRUE
EXCLUDE_FROM_ALL TRUE)

256
tests/QTestExtensions.h Normal file
View file

@ -0,0 +1,256 @@
//
// QTestExtensions.h
// tests/
//
// Created by Seiji Emery on 6/20/15.
// 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
//
#ifndef hifi_QTestExtensions_hpp
#define hifi_QTestExtensions_hpp
#include <QtTest/QtTest>
#include <functional>
// Implements several extensions to QtTest.
//
// Problems with QtTest:
// - QCOMPARE can compare float values (using a fuzzy compare), but uses an internal threshold
// that cannot be set explicitely (and we need explicit, adjustable error thresholds for our physics
// and math test code).
// - QFAIL takes a const char * failure message, and writing custom messages to it is complicated.
//
// To solve this, we have:
// - QCOMPARE_WITH_ABS_ERROR (compares floats, or *any other type* using explicitely defined error thresholds.
// To use it, you need to have a compareWithAbsError function ((T, T) -> V), and operator << for QTextStream).
// - QFAIL_WITH_MESSAGE("some " << streamed << " message"), which builds, writes to, and stringifies
// a QTextStream using black magic.
// - QCOMPARE_WITH_LAMBDA / QCOMPARE_WITH_FUNCTION, which implements QCOMPARE, but with a user-defined
// test function ((T, T) -> bool).
// - A simple framework to write additional custom test macros as needed (QCOMPARE is reimplemented
// from scratch using QTest::qFail, for example).
//
// Generates a QCOMPARE-style failure message that can be passed to QTest::qFail.
//
// Formatting looks like this:
// <qFail message> <failMessage....>
// Actual: (<stringified actual expr>) : <actual value>
// Expected: (<stringified expected expr>): <expected value>
// < additional messages (should be separated by "\n\t" for indent formatting)>
// Loc: [<file path....>(<linenum>)]
//
// Additional messages (after actual/expected) can be written using the std::function callback.
// If these messages span more than one line, wrap them with "\n\t" to get proper indentation / formatting)
//
template <typename T> inline
QString QTest_generateCompareFailureMessage (
const char* failMessage,
const T& actual, const T& expected,
const char* actual_expr, const char* expected_expr,
std::function<QTextStream& (QTextStream&)> writeAdditionalMessages
) {
QString s1 = actual_expr, s2 = expected_expr;
int pad1_ = qMax(s2.length() - s1.length(), 0);
int pad2_ = qMax(s1.length() - s2.length(), 0);
QString pad1 = QString(")").rightJustified(pad1_, ' ');
QString pad2 = QString(")").rightJustified(pad2_, ' ');
QString msg;
QTextStream stream (&msg);
stream << failMessage << "\n\t"
"Actual: (" << actual_expr << pad1 << ": " << actual << "\n\t"
"Expected: (" << expected_expr << pad2 << ": " << expected << "\n\t";
writeAdditionalMessages(stream);
return msg;
}
// Generates a QCOMPARE-style failure message that can be passed to QTest::qFail.
//
// Formatting looks like this:
// <qFail message> <failMessage....>
// Actual: (<stringified actual expr>) : <actual value>
// Expected: (<stringified expected expr>): <expected value>
// Loc: [<file path....>(<linenum>)]
// (no message callback)
//
template <typename T> inline
QString QTest_generateCompareFailureMessage (
const char* failMessage,
const T& actual, const T& expected,
const char* actual_expr, const char* expected_expr
) {
QString s1 = actual_expr, s2 = expected_expr;
int pad1_ = qMax(s2.length() - s1.length(), 0);
int pad2_ = qMax(s1.length() - s2.length(), 0);
QString pad1 = QString("): ").rightJustified(pad1_, ' ');
QString pad2 = QString("): ").rightJustified(pad2_, ' ');
QString msg;
QTextStream stream (&msg);
stream << failMessage << "\n\t"
"Actual: (" << actual_expr << pad1 << actual << "\n\t"
"Expected: (" << expected_expr << pad2 << expected;
return msg;
}
// Hacky function that can assemble a QString from a QTextStream via a callback
// (ie. stream operations w/out qDebug())
inline
QString makeMessageFromStream (std::function<void(QTextStream&)> writeMessage) {
QString msg;
QTextStream stream(&msg);
writeMessage(stream);
return msg;
}
inline
void QTest_failWithCustomMessage (
std::function<void(QTextStream&)> writeMessage, int line, const char* file
) {
QTest::qFail(qPrintable(makeMessageFromStream(writeMessage)), file, line);
}
// Equivalent to QFAIL, but takes a message that can be formatted using stream operators.
// Writes to a QTextStream internally, and calls QTest::qFail (the internal impl of QFAIL,
// with the current file and line number)
//
// example:
// inline void foo () {
// int thing = 2;
// QFAIL_WITH_MESSAGE("Message " << thing << ";");
// }
//
#define QFAIL_WITH_MESSAGE(...) \
do { \
QTest_failWithCustomMessage([&](QTextStream& stream) { stream << __VA_ARGS__; }, __LINE__, __FILE__); \
return; \
} while(0)
// Calls qFail using QTest_generateCompareFailureMessage.
// This is (usually) wrapped in macros, but if you call this directly you should return immediately to get QFAIL semantics.
template <typename T> inline
void QTest_failWithMessage(
const char* failMessage,
const T& actual, const T& expected,
const char* actualExpr, const char* expectedExpr,
int line, const char* file
) {
QTest::qFail(qPrintable(QTest_generateCompareFailureMessage(
failMessage, actual, expected, actualExpr, expectedExpr)), file, line);
}
// Calls qFail using QTest_generateCompareFailureMessage.
// This is (usually) wrapped in macros, but if you call this directly you should return immediately to get QFAIL semantics.
template <typename T> inline
void QTest_failWithMessage(
const char* failMessage,
const T& actual, const T& expected,
const char* actualExpr, const char* expectedExpr,
int line, const char* file,
std::function<QTextStream& (QTextStream&)> writeAdditionalMessageLines
) {
QTest::qFail(qPrintable(QTest_generateCompareFailureMessage(
failMessage, actual, expected, actualExpr, expectedExpr, writeAdditionalMessageLines)), file, line);
}
// Implements QCOMPARE_WITH_ABS_ERROR
template <typename T, typename V> inline
bool QTest_compareWithAbsError(
const T& actual, const T& expected,
const char* actual_expr, const char* expected_expr,
int line, const char* file,
const V& epsilon
) {
if (abs(getErrorDifference(actual, expected)) > abs(epsilon)) {
QTest_failWithMessage(
"Compared values are not the same (fuzzy compare)",
actual, expected, actual_expr, expected_expr, line, file,
[&] (QTextStream& stream) -> QTextStream& {
return stream << "Err tolerance: " << getErrorDifference((actual), (expected)) << " > " << epsilon;
});
return false;
}
return true;
}
// Implements a fuzzy QCOMPARE using an explicit epsilon error value.
// If you use this, you must have the following functions defined for the types you're using:
// <T, V> V compareWithAbsError (const T& a, const T& b) (should return the absolute, max difference between a and b)
// <T> QTextStream & operator << (QTextStream& stream, const T& value)
//
// Here's an implementation for glm::vec3:
// inline float compareWithAbsError (const glm::vec3 & a, const glm::vec3 & b) { // returns
// return glm::distance(a, b);
// }
// inline QTextStream & operator << (QTextStream & stream, const T & v) {
// return stream << "glm::vec3 { " << v.x << ", " << v.y << ", " << v.z << " }"
// }
//
#define QCOMPARE_WITH_ABS_ERROR(actual, expected, epsilon) \
do { \
if (!QTest_compareWithAbsError((actual), (expected), #actual, #expected, __LINE__, __FILE__, epsilon)) \
return; \
} while(0)
// Implements QCOMPARE using an explicit, externally defined test function.
// The advantage of this (over a manual check or what have you) is that the values of actual and
// expected are printed in the event that the test fails.
//
// testFunc(const T & actual, const T & expected) -> bool: true (test succeeds) | false (test fails)
//
#define QCOMPARE_WITH_FUNCTION(actual, expected, testFunc) \
do { \
if (!(testFunc((actual), (expected)))) { \
QTest_failWithMessage("Compared values are not the same", (actual), (expected), #actual, #expected, __LINE__, __FILE__); \
return; \
} \
} while (0)
// Implements QCOMPARE using an explicit, externally defined test function.
// Unlike QCOMPARE_WITH_FUNCTION, this func / closure takes no arguments (which is much more convenient
// if you're using a c++11 closure / lambda).
//
// usage:
// QCOMPARE_WITH_LAMBDA(foo, expectedFoo, [&foo, &expectedFoo] () {
// return foo->isFooish() && foo->fooishness() >= expectedFoo->fooishness();
// });
// (fails if foo is not as fooish as expectedFoo)
//
#define QCOMPARE_WITH_LAMBDA(actual, expected, testClosure) \
do { \
if (!(testClosure())) { \
QTest_failWithMessage("Compared values are not the same", (actual), (expected), #actual, #expected, __LINE__, __FILE__); \
return; \
} \
} while (0)
// Same as QCOMPARE_WITH_FUNCTION, but with a custom fail message
#define QCOMPARE_WITH_FUNCTION_AND_MESSAGE(actual, expected, testfunc, failMessage) \
do { \
if (!(testFunc((actual), (expected)))) { \
QTest_failWithMessage((failMessage), (actual), (expected), #actual, #expected, __LINE__, __FILE__); \
return; \
} \
} while (0)
// Same as QCOMPARE_WITH_FUNCTION, but with a custom fail message
#define QCOMPARE_WITH_LAMBDA_AND_MESSAGE(actual, expected, testClosure, failMessage) \
do { \
if (!(testClosure())) { \
QTest_failWithMessage((failMessage), (actual), (expected), #actual, #expected, __LINE__, __FILE__); \
return; \
} \
} while (0)
#endif

View file

@ -1,8 +1,9 @@
set(TARGET_NAME audio-tests)
# Declare dependencies
macro (SETUP_TESTCASE_DEPENDENCIES)
# link in the shared libraries
link_hifi_libraries(shared audio networking)
setup_hifi_project()
copy_dlls_beside_windows_executable()
endmacro ()
# link in the shared libraries
link_hifi_libraries(shared audio networking)
copy_dlls_beside_windows_executable()
setup_hifi_testcase()

View file

@ -13,10 +13,17 @@
#include "SharedUtil.h"
// Adds an implicit cast to make sure that actual and expected are of the same type.
// QCOMPARE(<unsigned int>, <int>) => cryptic linker error.
// (since QTest::qCompare is defined for (T, T, ...), but not (T, U, ...))
//
#define QCOMPARE_WITH_CAST(actual, expected) \
QCOMPARE(actual, static_cast<decltype(actual)>(expected))
QTEST_MAIN(AudioRingBufferTests)
void AudioRingBufferTests::assertBufferSize(const AudioRingBuffer& buffer, int samples) {
if (buffer.samplesAvailable() != samples) {
qDebug("Unexpected num samples available! Exptected: %d Actual: %d\n", samples, buffer.samplesAvailable());
}
QCOMPARE(buffer.samplesAvailable(), samples);
}
void AudioRingBufferTests::runAllTests() {
@ -54,13 +61,8 @@ void AudioRingBufferTests::runAllTests() {
// verify 143 samples of read data
for (int i = 0; i < 143; i++) {
if (readData[i] != i) {
qDebug("first readData[%d] incorrect! Expcted: %d Actual: %d", i, i, readData[i]);
return;
}
QCOMPARE(readData[i], (int16_t)i);
}
writeIndexAt = 0;
readIndexAt = 0;
@ -81,9 +83,6 @@ void AudioRingBufferTests::runAllTests() {
readData[i] = writeIndexAt - 100 + i;
}
writeIndexAt = 0;
readIndexAt = 0;
@ -96,50 +95,33 @@ void AudioRingBufferTests::runAllTests() {
assertBufferSize(ringBuffer, 100);
// write 29 silent samples, 100 samples in buffer, make sure non were added
int samplesWritten;
if ((samplesWritten = ringBuffer.addSilentSamples(29)) != 0) {
qDebug("addSilentSamples(29) incorrect! Expected: 0 Actual: %d", samplesWritten);
return;
}
int samplesWritten = ringBuffer.addSilentSamples(29);
QCOMPARE(samplesWritten, 0);
assertBufferSize(ringBuffer, 100);
// read 3 samples, 97 samples in buffer (expect to read "1", "2", "3")
readIndexAt += ringBuffer.readSamples(&readData[readIndexAt], 3);
for (int i = 0; i < 3; i++) {
if (readData[i] != i + 1) {
qDebug("Second readData[%d] incorrect! Expcted: %d Actual: %d", i, i + 1, readData[i]);
return;
}
QCOMPARE(readData[i], static_cast<int16_t>(i + 1));
}
assertBufferSize(ringBuffer, 97);
// write 4 silent samples, 100 samples in buffer
if ((samplesWritten = ringBuffer.addSilentSamples(4)) != 3) {
qDebug("addSilentSamples(4) incorrect! Exptected: 3 Actual: %d", samplesWritten);
return;
}
QCOMPARE(ringBuffer.addSilentSamples(4), 3);
assertBufferSize(ringBuffer, 100);
// read back 97 samples (the non-silent samples), 3 samples in buffer (expect to read "4" thru "100")
readIndexAt += ringBuffer.readSamples(&readData[readIndexAt], 97);
for (int i = 3; i < 100; i++) {
if (readData[i] != i + 1) {
qDebug("third readData[%d] incorrect! Expcted: %d Actual: %d", i, i + 1, readData[i]);
return;
}
QCOMPARE(readData[i], static_cast<int16_t>(i + 1));
}
assertBufferSize(ringBuffer, 3);
// read back 3 silent samples, 0 samples in buffer
readIndexAt += ringBuffer.readSamples(&readData[readIndexAt], 3);
for (int i = 100; i < 103; i++) {
if (readData[i] != 0) {
qDebug("Fourth readData[%d] incorrect! Expcted: %d Actual: %d", i, 0, readData[i]);
return;
}
QCOMPARE(readData[i], static_cast<int16_t>(0));
}
assertBufferSize(ringBuffer, 0);
}
qDebug() << "PASSED";
}

View file

@ -12,13 +12,16 @@
#ifndef hifi_AudioRingBufferTests_h
#define hifi_AudioRingBufferTests_h
#include <QtTest/QtTest>
#include "AudioRingBuffer.h"
namespace AudioRingBufferTests {
class AudioRingBufferTests : public QObject {
Q_OBJECT
private slots:
void runAllTests();
private:
void assertBufferSize(const AudioRingBuffer& buffer, int samples);
};

View file

@ -1,19 +0,0 @@
//
// main.cpp
// tests/audio/src
//
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AudioRingBufferTests.h"
#include <stdio.h>
int main(int argc, char** argv) {
AudioRingBufferTests::runAllTests();
printf("all tests passed. press enter to exit\n");
getchar();
return 0;
}

View file

@ -1,8 +1,10 @@
set(TARGET_NAME jitter-tests)
setup_hifi_project()
# Declare dependencies
macro (setup_testcase_dependencies)
# link in the shared libraries
link_hifi_libraries(shared networking)
# link in the shared libraries
link_hifi_libraries(shared networking)
copy_dlls_beside_windows_executable()
endmacro()
copy_dlls_beside_windows_executable()
setup_hifi_testcase()

View file

@ -23,12 +23,24 @@
#include <SimpleMovingAverage.h>
#include <StDev.h>
#include "JitterTests.h"
// Uncomment this to run manually
//#define RUN_MANUALLY
#ifndef RUN_MANUALLY
QTEST_MAIN(JitterTests)
#else // RUN_MANUALLY
const quint64 MSEC_TO_USEC = 1000;
const quint64 LARGE_STATS_TIME = 500; // we don't expect stats calculation to take more than this many usecs
void runSend(const char* addressOption, int port, int gap, int size, int report);
void runReceive(const char* addressOption, int port, int gap, int size, int report);
int main(int argc, const char * argv[]) {
if (argc != 7) {
printf("usage: jitter-tests <--send|--receive> <address> <port> <gap in usecs> <packet size> <report interval in msecs>\n");
@ -375,3 +387,5 @@ void runReceive(const char* addressOption, int port, int gap, int size, int repo
WSACleanup();
#endif
}
#endif // #ifdef RUN_MANUALLY

View file

@ -0,0 +1,26 @@
//
// QTestExtensions.h
// tests/jitter/src
//
// 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
//
#ifndef hifi_JitterTests_h
#define hifi_JitterTests_h
#include <QtTest/QtTest>
class JitterTests : public QObject {
Q_OBJECT
private slots:
void qTestsNotYetImplemented () {
qDebug() << "TODO: Reimplement this using QtTest!\n"
"(JitterTests takes commandline arguments (port numbers), and can be run manually by #define-ing RUN_MANUALLY in JitterTests.cpp)";
}
};
#endif

View file

@ -1,8 +1,10 @@
set(TARGET_NAME networking-tests)
setup_hifi_project()
# Declare dependencies
macro (setup_testcase_dependencies)
# link in the shared libraries
link_hifi_libraries(shared networking)
# link in the shared libraries
link_hifi_libraries(shared networking)
copy_dlls_beside_windows_executable()
endmacro ()
copy_dlls_beside_windows_executable()
setup_hifi_testcase()

View file

@ -1,5 +1,5 @@
//
// AudioRingBufferTests.cpp
// SequenceNumberStatsTests.cpp
// tests/networking/src
//
// Created by Yixin Wang on 6/24/2014
@ -9,24 +9,24 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <cassert>
#include <limits>
#include <SharedUtil.h>
#include "SequenceNumberStatsTests.h"
void SequenceNumberStatsTests::runAllTests() {
rolloverTest();
earlyLateTest();
duplicateTest();
pruneTest();
resyncTest();
}
QTEST_MAIN(SequenceNumberStatsTests)
const quint32 UINT16_RANGE = std::numeric_limits<quint16>::max() + 1;
// Adds an implicit cast to make sure that actual and expected are of the same type.
// QCOMPARE(<unsigned int>, <int>) => cryptic linker error.
// (since QTest::qCompare is defined for (T, T, ...), but not (T, U, ...))
//
#define QCOMPARE_WITH_CAST(actual, expected) \
QCOMPARE(actual, static_cast<decltype(actual)>(expected))
void SequenceNumberStatsTests::rolloverTest() {
SequenceNumberStats stats;
@ -39,11 +39,12 @@ void SequenceNumberStatsTests::rolloverTest() {
stats.sequenceNumberReceived(seq);
seq = seq + (quint16)1;
assert(stats.getEarly() == 0);
assert(stats.getLate() == 0);
assert(stats.getLost() == 0);
assert(stats.getReceived() == i + 1);
assert(stats.getRecovered() == 0);
QCOMPARE_WITH_CAST(stats.getEarly(), 0);
QCOMPARE_WITH_CAST(stats.getEarly(), 0);
QCOMPARE_WITH_CAST(stats.getLate(), 0);
QCOMPARE_WITH_CAST(stats.getLost(), 0);
QCOMPARE_WITH_CAST(stats.getReceived(), i + 1);
QCOMPARE_WITH_CAST(stats.getRecovered(), 0);
}
stats.reset();
}
@ -69,11 +70,11 @@ void SequenceNumberStatsTests::earlyLateTest() {
seq = seq + (quint16)1;
numSent++;
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == numRecovered);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), numRecovered);
}
// skip 10
@ -88,11 +89,11 @@ void SequenceNumberStatsTests::earlyLateTest() {
seq = seq + (quint16)1;
numSent++;
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == numRecovered);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), numRecovered);
}
// send ones we skipped
@ -104,11 +105,11 @@ void SequenceNumberStatsTests::earlyLateTest() {
numLost--;
numRecovered++;
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == numRecovered);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), numRecovered);
}
}
stats.reset();
@ -142,12 +143,12 @@ void SequenceNumberStatsTests::duplicateTest() {
seq = seq + (quint16)1;
numSent++;
assert(stats.getUnreasonable() == numDuplicate);
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), numDuplicate);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), 0);
}
// skip 10
@ -164,12 +165,12 @@ void SequenceNumberStatsTests::duplicateTest() {
seq = seq + (quint16)1;
numSent++;
assert(stats.getUnreasonable() == numDuplicate);
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), numDuplicate);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), 0);
}
// send 5 duplicates from before skip
@ -180,12 +181,12 @@ void SequenceNumberStatsTests::duplicateTest() {
numDuplicate++;
numLate++;
assert(stats.getUnreasonable() == numDuplicate);
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), numDuplicate);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), 0);
}
// send 5 duplicates from after skip
@ -196,12 +197,12 @@ void SequenceNumberStatsTests::duplicateTest() {
numDuplicate++;
numLate++;
assert(stats.getUnreasonable() == numDuplicate);
assert(stats.getEarly() == numEarly);
assert(stats.getLate() == numLate);
assert(stats.getLost() == numLost);
assert(stats.getReceived() == numSent);
assert(stats.getRecovered() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), numDuplicate);
QCOMPARE_WITH_CAST(stats.getEarly(), numEarly);
QCOMPARE_WITH_CAST(stats.getLate(), numLate);
QCOMPARE_WITH_CAST(stats.getLost(), numLost);
QCOMPARE_WITH_CAST(stats.getReceived(), numSent);
QCOMPARE_WITH_CAST(stats.getRecovered(), 0);
}
}
stats.reset();
@ -253,22 +254,22 @@ void SequenceNumberStatsTests::pruneTest() {
numLost += 10;
const QSet<quint16>& missingSet = stats.getMissingSet();
assert(missingSet.size() <= 1000);
QCOMPARE_WITH_CAST(missingSet.size() <= 1000, true);
if (missingSet.size() > 1000) {
qDebug() << "FAIL: missingSet larger than 1000.";
}
for (int i = 0; i < 10; i++) {
assert(missingSet.contains(highestSkipped2));
QCOMPARE_WITH_CAST(missingSet.contains(highestSkipped2), true);
highestSkipped2 = highestSkipped2 - (quint16)1;
}
for (int i = 0; i < 989; i++) {
assert(missingSet.contains(highestSkipped));
QCOMPARE_WITH_CAST(missingSet.contains(highestSkipped), true);
highestSkipped = highestSkipped - (quint16)1;
}
for (int i = 0; i < 11; i++) {
assert(!missingSet.contains(highestSkipped));
QCOMPARE_WITH_CAST(!missingSet.contains(highestSkipped), true);
highestSkipped = highestSkipped - (quint16)1;
}
}
@ -288,7 +289,7 @@ void SequenceNumberStatsTests::resyncTest() {
sequence = 89;
stats.sequenceNumberReceived(sequence);
assert(stats.getUnreasonable() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), 0);
sequence = 2990;
for (int i = 0; i < 10; i++) {
@ -296,7 +297,7 @@ void SequenceNumberStatsTests::resyncTest() {
sequence += (quint16)1;
}
assert(stats.getUnreasonable() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), 0);
sequence = 0;
@ -305,7 +306,7 @@ void SequenceNumberStatsTests::resyncTest() {
sequence += (quint16)1;
}
assert(stats.getUnreasonable() == 7);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), 7);
sequence = 6000;
for (int R = 0; R < 7; R++) {
@ -313,12 +314,12 @@ void SequenceNumberStatsTests::resyncTest() {
sequence += (quint16)1;
}
assert(stats.getUnreasonable() == 14);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), 14);
sequence = 9000;
for (int i = 0; i < 10; i++) {
stats.sequenceNumberReceived(sequence);
sequence += (quint16)1;
}
assert(stats.getUnreasonable() == 0);
QCOMPARE_WITH_CAST(stats.getUnreasonable(), 0);
}

View file

@ -1,5 +1,5 @@
//
// AudioRingBufferTests.h
// SequenceNumberStatsTests.h
// tests/networking/src
//
// Created by Yixin Wang on 6/24/2014
@ -12,13 +12,14 @@
#ifndef hifi_SequenceNumberStatsTests_h
#define hifi_SequenceNumberStatsTests_h
#include <QtTest/QtTest>
#include "SequenceNumberStatsTests.h"
#include "SequenceNumberStats.h"
namespace SequenceNumberStatsTests {
void runAllTests();
class SequenceNumberStatsTests : public QObject {
Q_OBJECT
private slots:
void rolloverTest();
void earlyLateTest();
void duplicateTest();

View file

@ -1,19 +0,0 @@
//
// main.cpp
// tests/networking/src
//
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SequenceNumberStatsTests.h"
#include <stdio.h>
int main(int argc, char** argv) {
SequenceNumberStatsTests::runAllTests();
printf("tests passed! press enter to exit");
getchar();
return 0;
}

View file

@ -1,8 +1,10 @@
set(TARGET_NAME octree-tests)
setup_hifi_project(Script Network)
# Declare dependencies
macro (setup_testcase_dependencies)
# link in the shared libraries
link_hifi_libraries(shared octree gpu model fbx networking environment entities avatars audio animation script-engine physics)
# link in the shared libraries
link_hifi_libraries(shared octree gpu model fbx networking environment entities avatars audio animation script-engine physics)
copy_dlls_beside_windows_executable()
endmacro ()
copy_dlls_beside_windows_executable()
setup_hifi_testcase(Script Network)

View file

@ -9,92 +9,63 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <AABox.h>
#include <AACube.h>
#include "AABoxCubeTests.h"
void AABoxCubeTests::AABoxCubeTests(bool verbose) {
qDebug() << "******************************************************************************************";
qDebug() << "AABoxCubeTests::AABoxCubeTests()";
QTEST_MAIN(AABoxCubeTests)
{
qDebug() << "Test 1: AABox.findRayIntersection() inside out MIN_X_FACE";
void AABoxCubeTests::raycastOutHitsXMinFace() {
// Raycast inside out
glm::vec3 corner(0.0f, 0.0f, 0.0f);
float size = 1.0f;
AABox box(corner, size);
glm::vec3 origin(0.5f, 0.5f, 0.5f);
glm::vec3 direction(-1.0f, 0.0f, 0.0f);
float distance;
BoxFace face;
glm::vec3 corner(0.0f, 0.0f, 0.0f);
float size = 1.0f;
AABox box(corner, size);
glm::vec3 origin(0.5f, 0.5f, 0.5f);
glm::vec3 direction(-1.0f, 0.0f, 0.0f);
float distance;
BoxFace face;
bool intersects = box.findRayIntersection(origin, direction, distance, face);
if (intersects && distance == 0.5f && face == MIN_X_FACE) {
qDebug() << "Test 1: PASSED";
} else {
qDebug() << "intersects=" << intersects << "expected=" << true;
qDebug() << "distance=" << distance << "expected=" << 0.5f;
qDebug() << "face=" << face << "expected=" << MIN_X_FACE;
}
}
{
qDebug() << "Test 2: AABox.findRayIntersection() inside out MAX_X_FACE";
glm::vec3 corner(0.0f, 0.0f, 0.0f);
float size = 1.0f;
AABox box(corner, size);
glm::vec3 origin(0.5f, 0.5f, 0.5f);
glm::vec3 direction(1.0f, 0.0f, 0.0f);
float distance;
BoxFace face;
bool intersects = box.findRayIntersection(origin, direction, distance, face);
if (intersects && distance == 0.5f && face == MAX_X_FACE) {
qDebug() << "Test 2: PASSED";
} else {
qDebug() << "intersects=" << intersects << "expected=" << true;
qDebug() << "distance=" << distance << "expected=" << 0.5f;
qDebug() << "face=" << face << "expected=" << MAX_X_FACE;
}
}
{
qDebug() << "Test 3: AABox.findRayIntersection() outside in";
glm::vec3 corner(0.5f, 0.0f, 0.0f);
float size = 0.5f;
AABox box(corner, size);
glm::vec3 origin(0.25f, 0.25f, 0.25f);
glm::vec3 direction(1.0f, 0.0f, 0.0f);
float distance;
BoxFace face;
bool intersects = box.findRayIntersection(origin, direction, distance, face);
if (intersects && distance == 0.25f && face == MIN_X_FACE) {
qDebug() << "Test 3: PASSED";
} else {
qDebug() << "intersects=" << intersects << "expected=" << true;
qDebug() << "distance=" << distance << "expected=" << 0.5f;
qDebug() << "face=" << face << "expected=" << MIN_X_FACE;
}
}
qDebug() << "******************************************************************************************";
bool intersects = box.findRayIntersection(origin, direction, distance, face);
QCOMPARE(intersects, true);
QCOMPARE(distance, 0.5f);
QCOMPARE(face, MIN_X_FACE);
}
void AABoxCubeTests::runAllTests(bool verbose) {
AABoxCubeTests(verbose);
void AABoxCubeTests::raycastOutHitsXMaxFace () {
// Raycast inside out
glm::vec3 corner(0.0f, 0.0f, 0.0f);
float size = 1.0f;
AABox box(corner, size);
glm::vec3 origin(0.5f, 0.5f, 0.5f);
glm::vec3 direction(1.0f, 0.0f, 0.0f);
float distance;
BoxFace face;
bool intersects = box.findRayIntersection(origin, direction, distance, face);
QCOMPARE(intersects, true);
QCOMPARE(distance, 0.5f);
QCOMPARE(face, MAX_X_FACE);
}
void AABoxCubeTests::raycastInHitsXMinFace () {
// Raycast outside in
glm::vec3 corner(0.5f, 0.0f, 0.0f);
float size = 0.5f;
AABox box(corner, size);
glm::vec3 origin(0.25f, 0.25f, 0.25f);
glm::vec3 direction(1.0f, 0.0f, 0.0f);
float distance;
BoxFace face;
bool intersects = box.findRayIntersection(origin, direction, distance, face);
QCOMPARE(intersects, true);
QCOMPARE(distance, 0.25f);
QCOMPARE(face, MIN_X_FACE);
}

View file

@ -12,9 +12,18 @@
#ifndef hifi_AABoxCubeTests_h
#define hifi_AABoxCubeTests_h
namespace AABoxCubeTests {
void AABoxCubeTests(bool verbose);
void runAllTests(bool verbose);
}
#include <QtTest/QtTest>
class AABoxCubeTests : public QObject {
Q_OBJECT
private slots:
void raycastOutHitsXMinFace ();
void raycastOutHitsXMaxFace ();
void raycastInHitsXMinFace ();
// TODO: Add more unit tests!
// (do we need this? Currently no tests for no-intersection or non-orthogonal rays)
};
#endif // hifi_AABoxCubeTests_h

View file

@ -25,6 +25,9 @@
//#include "EntityTests.h"
#include "ModelTests.h" // needs to be EntityTests.h soon
QTEST_MAIN(EntityTests)
/*
void EntityTests::entityTreeTests(bool verbose) {
bool extraVerbose = false;
@ -508,4 +511,4 @@ void EntityTests::entityTreeTests(bool verbose) {
void EntityTests::runAllTests(bool verbose) {
entityTreeTests(verbose);
}
*/

View file

@ -12,9 +12,21 @@
#ifndef hifi_EntityTests_h
#define hifi_EntityTests_h
namespace EntityTests {
void entityTreeTests(bool verbose = false);
void runAllTests(bool verbose = false);
}
#include <QtTest/QtTest>
//
// TODO: These are waaay out of date with the current codebase, and should be reimplemented at some point.
//
class EntityTests : public QObject {
Q_OBJECT
private slots:
void testsNotImplemented () {
qDebug() << "fixme: ModelTests are currently broken and need to be reimplemented";
}
// void entityTreeTests(bool verbose = false);
// void runAllTests(bool verbose = false);
};
#endif // hifi_EntityTests_h

File diff suppressed because it is too large Load diff

View file

@ -12,13 +12,27 @@
#ifndef hifi_OctreeTests_h
#define hifi_OctreeTests_h
namespace OctreeTests {
#include <QtTest/QtTest>
void propertyFlagsTests(bool verbose);
void byteCountCodingTests(bool verbose);
void modelItemTests(bool verbose);
class OctreeTests : public QObject {
Q_OBJECT
private slots:
// FIXME: These two tests are broken and need to be fixed / updated
void propertyFlagsTests();
void byteCountCodingTests();
// This test is fine
void modelItemTests();
void runAllTests(bool verbose);
// TODO: Break these into separate test functions
};
// Helper functions
inline QByteArray makeQByteArray (std::initializer_list<char> bytes) {
return QByteArray {
bytes.begin(), static_cast<int>(bytes.size() * sizeof(char))
};
}
#endif // hifi_OctreeTests_h

View file

@ -1,24 +0,0 @@
//
// main.cpp
// tests/octree/src
//
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AABoxCubeTests.h"
#include "ModelTests.h" // needs to be EntityTests.h soon
#include "OctreeTests.h"
#include "SharedUtil.h"
int main(int argc, const char* argv[]) {
const char* VERBOSE = "--verbose";
bool verbose = cmdOptionExists(argc, argv, VERBOSE);
qDebug() << "OctreeTests::runAllTests()";
//OctreeTests::runAllTests(verbose);
//AABoxCubeTests::runAllTests(verbose);
EntityTests::runAllTests(verbose);
return 0;
}

View file

@ -1,17 +1,18 @@
set(TARGET_NAME physics-tests)
setup_hifi_project()
# Declare dependencies
macro (SETUP_TESTCASE_DEPENDENCIES)
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
add_dependency_external_projects(bullet)
find_package(Bullet REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
link_hifi_libraries(shared physics)
copy_dlls_beside_windows_executable()
endmacro ()
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
add_dependency_external_projects(bullet)
find_package(Bullet REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
link_hifi_libraries(shared physics)
copy_dlls_beside_windows_executable()
setup_hifi_testcase(Script)

Some files were not shown because too many files have changed in this diff Show more