Merge pull request #12365 from AndrewMeadows/workload-001

moving Job/Task framework out of 'render' library and into its own 'task' library
This commit is contained in:
Andrew Meadows 2018-02-13 12:26:32 -08:00 committed by GitHub
commit b111e82763
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 568 additions and 63 deletions

View file

@ -1,6 +1,6 @@
set(TARGET_NAME native-lib)
setup_hifi_library()
link_hifi_libraries(shared networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
link_hifi_libraries(shared task networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
target_opengl()
target_bullet()

View file

@ -204,13 +204,14 @@ endif()
# link required hifi libraries
link_hifi_libraries(
shared octree ktx gpu gl procedural graphics render
shared task octree ktx gpu gl procedural graphics render
pointers
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
workload
${PLATFORM_GL_BACKEND}
)

View file

@ -13,5 +13,6 @@ include_hifi_library_headers(entities-renderer)
include_hifi_library_headers(audio)
include_hifi_library_headers(entities)
include_hifi_library_headers(octree)
include_hifi_library_headers(task)
target_bullet()

View file

@ -13,6 +13,7 @@ include_hifi_library_headers(fbx)
include_hifi_library_headers(entities)
include_hifi_library_headers(avatars)
include_hifi_library_headers(controllers)
include_hifi_library_headers(task)
target_bullet()
target_polyvox()

View file

@ -913,18 +913,25 @@ void EntityTree::findEntities(RecurseOctreeOperation& elementFilter,
recurseTreeWithOperation(elementFilter, nullptr);
}
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) {
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) const {
EntityItemID entityID(id);
return findEntityByEntityItemID(entityID);
}
EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /*const*/ {
EntityItemPointer foundEntity = NULL;
EntityTreeElementPointer containingElement = getContainingElement(entityID);
if (containingElement) {
foundEntity = containingElement->getEntityWithEntityItemID(entityID);
EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) const {
EntityItemPointer foundEntity = nullptr;
{
QReadLocker locker(&_entityMapLock);
foundEntity = _entityMap.value(entityID);
}
if (foundEntity && !foundEntity->getElement()) {
// special case to maintain legacy behavior:
// if the entity is in the map but not in the tree
// then pretend the entity doesn't exist
return EntityItemPointer(nullptr);
} else {
return foundEntity;
}
return foundEntity;
}
void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties) {

View file

@ -132,9 +132,9 @@ public:
/// \param position point of query in world-frame (meters)
/// \param targetRadius radius of query (meters)
EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius);
EntityItemPointer findEntityByID(const QUuid& id);
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID);
virtual SpatiallyNestablePointer findByID(const QUuid& id) override { return findEntityByID(id); }
EntityItemPointer findEntityByID(const QUuid& id) const;
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const;
virtual SpatiallyNestablePointer findByID(const QUuid& id) const override { return findEntityByID(id); }
EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID

View file

@ -3,10 +3,10 @@ AUTOSCRIBE_SHADER_LIB(gpu graphics render)
# pull in the resources.qrc file
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
setup_hifi_library(Gui Network Qml Quick Script)
link_hifi_libraries(shared ktx gpu graphics model-networking render animation fbx image procedural)
link_hifi_libraries(shared task ktx gpu graphics model-networking render animation fbx image procedural)
include_hifi_library_headers(audio)
include_hifi_library_headers(networking)
include_hifi_library_headers(octree)
include_hifi_library_headers(audio)
if (NOT ANDROID)
target_nsight()

View file

@ -88,4 +88,4 @@ protected:
};
#endif
#endif

View file

@ -3,6 +3,6 @@ AUTOSCRIBE_SHADER_LIB(gpu graphics)
setup_hifi_library()
# render needs octree only for getAccuracyAngle(float, int)
link_hifi_libraries(shared ktx gpu graphics octree)
link_hifi_libraries(shared task ktx gpu graphics octree)
target_nsight()

View file

@ -14,9 +14,10 @@
#include <SettingHandle.h>
#include "Scene.h"
#include "../task/Task.h"
#include <gpu/Batch.h>
#include <task/Task.h>
#include "Scene.h"
namespace render {
@ -24,6 +25,7 @@ namespace render {
class RenderContext : public task::JobContext {
public:
RenderContext() : task::JobContext(trace_render()) {}
virtual ~RenderContext() {}
RenderArgs* args;
@ -100,7 +102,7 @@ namespace render {
protected:
RenderContextPointer _renderContext;
void run(const RenderContextPointer& context) override { assert(_renderContext); Task::run(_renderContext); }
};
using EnginePointer = std::shared_ptr<Engine>;

View file

@ -55,4 +55,4 @@ namespace render {
};
}
#endif // hifi_render_SortTask_h;
#endif // hifi_render_SortTask_h;

View file

@ -27,6 +27,7 @@ Q_LOGGING_CATEGORY(trace_simulation_animation, "trace.simulation.animation")
Q_LOGGING_CATEGORY(trace_simulation_animation_detail, "trace.simulation.animation.detail")
Q_LOGGING_CATEGORY(trace_simulation_physics, "trace.simulation.physics")
Q_LOGGING_CATEGORY(trace_simulation_physics_detail, "trace.simulation.physics.detail")
Q_LOGGING_CATEGORY(trace_workload, "trace.workload")
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"

View file

@ -32,6 +32,7 @@ Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation)
Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation_detail)
Q_DECLARE_LOGGING_CATEGORY(trace_simulation_physics)
Q_DECLARE_LOGGING_CATEGORY(trace_simulation_physics_detail)
Q_DECLARE_LOGGING_CATEGORY(trace_workload)
class Duration {
public:

View file

@ -21,7 +21,7 @@ using SpatiallyNestableWeakPointer = std::weak_ptr<SpatiallyNestable>;
using SpatiallyNestablePointer = std::shared_ptr<SpatiallyNestable>;
class SpatialParentTree {
public:
virtual SpatiallyNestablePointer findByID(const QUuid& id) { return nullptr; }
virtual SpatiallyNestablePointer findByID(const QUuid& id) const { return nullptr; }
};
class SpatialParentFinder : public Dependency {

View file

@ -0,0 +1,3 @@
set(TARGET_NAME task)
setup_hifi_library()
link_hifi_libraries(shared)

View file

@ -20,8 +20,6 @@
#include "SettingHandle.h"
#include "Logging.h"
namespace task {
class JobConcept;
@ -140,12 +138,12 @@ public:
TaskConfig(bool enabled) : JobConfig(enabled) {}
// Get a sub job config through task.getConfig(path)
// where path can be:
// - <job_name> search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root)
// - <parent_name>.[<sub_parent_names>.]<job_name>
// Allowing to first look for the parent_name job (from this task as root) and then search from there for the
// Allowing to first look for the parent_name job (from this task as root) and then search from there for the
// optional sub_parent_names and finally from there looking for the job_name (assuming every job in the path were found)
//
// getter for qml integration, prefer the templated getter
@ -174,7 +172,7 @@ public:
void connectChildConfig(QConfigPointer childConfig, const std::string& name);
void transferChildrenConfigs(QConfigPointer source);
JobConcept* _task;
public slots:
@ -182,7 +180,7 @@ public slots:
};
using QConfigPointer = std::shared_ptr<QObject>;
}
#endif // hifi_task_Config_h

View file

@ -1,6 +1,6 @@
//
// Task.h
// render/src/task
// task/src/task
//
// Created by Zach Pomerantz on 1/6/2016.
// Copyright 2016 High Fidelity, Inc.
@ -17,23 +17,25 @@
#include "SettingHandle.h"
#include "Logging.h"
#include <Profile.h>
#include <PerfStat.h>
namespace task {
class JobConcept;
template <class RC> class JobT;
template <class RC> class TaskT;
template <class JC> class JobT;
template <class JC> class TaskT;
class JobNoIO {};
class JobContext {
public:
JobContext(const QLoggingCategory& category) : profileCategory(category) {
assert(&category);
}
virtual ~JobContext() {}
std::shared_ptr<JobConfig> jobConfig { nullptr };
const QLoggingCategory& profileCategory;
};
using JobContextPointer = std::shared_ptr<JobContext>;
@ -68,23 +70,23 @@ template<class T> void jobConfigure(T&, const TaskConfig&) {
// nop, as the default TaskConfig was used, so the data does not need a configure method
}
template <class T, class RC> void jobRun(T& data, const RC& renderContext, const JobNoIO& input, JobNoIO& output) {
data.run(renderContext);
template <class T, class JC> void jobRun(T& data, const JC& jobContext, const JobNoIO& input, JobNoIO& output) {
data.run(jobContext);
}
template <class T, class RC, class I> void jobRun(T& data, const RC& renderContext, const I& input, JobNoIO& output) {
data.run(renderContext, input);
template <class T, class JC, class I> void jobRun(T& data, const JC& jobContext, const I& input, JobNoIO& output) {
data.run(jobContext, input);
}
template <class T, class RC, class O> void jobRun(T& data, const RC& renderContext, const JobNoIO& input, O& output) {
data.run(renderContext, output);
template <class T, class JC, class O> void jobRun(T& data, const JC& jobContext, const JobNoIO& input, O& output) {
data.run(jobContext, output);
}
template <class T, class RC, class I, class O> void jobRun(T& data, const RC& renderContext, const I& input, O& output) {
data.run(renderContext, input, output);
template <class T, class JC, class I, class O> void jobRun(T& data, const JC& jobContext, const I& input, O& output) {
data.run(jobContext, input, output);
}
template <class RC>
template <class JC>
class Job {
public:
using Context = RC;
using Context = JC;
using ContextPointer = std::shared_ptr<Context>;
using Config = JobConfig;
using None = JobNoIO;
@ -94,7 +96,7 @@ public:
Concept(QConfigPointer config) : JobConcept(config) {}
virtual ~Concept() = default;
virtual void run(const ContextPointer& renderContext) = 0;
virtual void run(const ContextPointer& jobContext) = 0;
};
using ConceptPointer = std::shared_ptr<Concept>;
@ -130,12 +132,12 @@ public:
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
}
void run(const ContextPointer& renderContext) override {
renderContext->jobConfig = std::static_pointer_cast<Config>(Concept::_config);
if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) {
jobRun(_data, renderContext, _input.get<I>(), _output.edit<O>());
void run(const ContextPointer& jobContext) override {
jobContext->jobConfig = std::static_pointer_cast<Config>(Concept::_config);
if (jobContext->jobConfig->alwaysEnabled || jobContext->jobConfig->isEnabled()) {
jobRun(_data, jobContext, _input.get<I>(), _output.edit<O>());
}
renderContext->jobConfig.reset();
jobContext->jobConfig.reset();
}
};
template <class T, class I, class C = Config> using ModelI = Model<T, C, I, None>;
@ -161,12 +163,13 @@ public:
return concept->_data;
}
virtual void run(const ContextPointer& renderContext) {
virtual void run(const ContextPointer& jobContext) {
PerformanceTimer perfTimer(_name.c_str());
PROFILE_RANGE(render, _name.c_str());
// NOTE: rather than use the PROFILE_RANGE macro, we create a Duration manually
Duration profileRange(jobContext->profileCategory, _name.c_str());
auto start = usecTimestampNow();
_concept->run(renderContext);
_concept->run(jobContext);
_concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0);
}
@ -183,16 +186,16 @@ protected:
// It can be created on any type T by aliasing the type JobModel in the class T
// using JobModel = Task::Model<T>
// The class T is expected to have a "build" method acting as a constructor.
// The build method is where child Jobs can be added internally to the task
// The build method is where child Jobs can be added internally to the task
// where the input of the task can be setup to feed the child jobs
// and where the output of the task is defined
template <class RC>
class Task : public Job<RC> {
template <class JC>
class Task : public Job<JC> {
public:
using Context = RC;
using Context = JC;
using ContextPointer = std::shared_ptr<Context>;
using Config = TaskConfig;
using JobType = Job<RC>;
using JobType = Job<JC>;
using None = typename JobType::None;
using Concept = typename JobType::Concept;
using ConceptPointer = typename JobType::ConceptPointer;
@ -300,11 +303,11 @@ public:
}
}
void run(const ContextPointer& renderContext) override {
void run(const ContextPointer& jobContext) override {
auto config = std::static_pointer_cast<C>(Concept::_config);
if (config->alwaysEnabled || config->enabled) {
for (auto job : TaskConcept::_jobs) {
job.run(renderContext);
job.run(jobContext);
}
}
}

View file

@ -0,0 +1,5 @@
set(TARGET_NAME workload)
setup_hifi_library()
link_hifi_libraries(shared)

View file

@ -0,0 +1,94 @@
//
// Space.h
// libraries/shared/src/
//
// Created by Andrew Meadows 2018.01.30
// Copyright 2018 High Fidelity, Inc.
//
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
// Simple plane class.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Space.h"
#include <algorithm>
#include <glm/gtx/quaternion.hpp>
using namespace workload;
int32_t Space::createProxy(const Space::Sphere& newSphere) {
if (_freeIndices.empty()) {
_proxies.emplace_back(Space::Proxy(newSphere));
return (int32_t)_proxies.size() - 1;
} else {
int32_t index = _freeIndices.back();
_freeIndices.pop_back();
_proxies[index].sphere = newSphere;
_proxies[index].region = Space::REGION_UNKNOWN;
_proxies[index].prevRegion = Space::REGION_UNKNOWN;
return index;
}
}
void Space::deleteProxy(int32_t proxyId) {
if (proxyId >= (int32_t)_proxies.size() || _proxies.empty()) {
return;
}
if (proxyId == (int32_t)_proxies.size() - 1) {
// remove proxy on back
_proxies.pop_back();
if (!_freeIndices.empty()) {
// remove any freeIndices on back
std::sort(_freeIndices.begin(), _freeIndices.end());
while(!_freeIndices.empty() && _freeIndices.back() == (int32_t)_proxies.size() - 1) {
_freeIndices.pop_back();
_proxies.pop_back();
}
}
} else {
_proxies[proxyId].region = Space::REGION_INVALID;
_freeIndices.push_back(proxyId);
}
}
void Space::updateProxy(int32_t proxyId, const Space::Sphere& newSphere) {
if (proxyId >= (int32_t)_proxies.size()) {
return;
}
_proxies[proxyId].sphere = newSphere;
}
void Space::setViews(const std::vector<Space::View>& views) {
_views = views;
}
void Space::categorizeAndGetChanges(std::vector<Space::Change>& changes) {
uint32_t numProxies = (uint32_t)_proxies.size();
uint32_t numViews = (uint32_t)_views.size();
for (uint32_t i = 0; i < numProxies; ++i) {
Proxy& proxy = _proxies[i];
if (proxy.region < Space::REGION_INVALID) {
uint8_t region = Space::REGION_UNKNOWN;
for (uint32_t j = 0; j < numViews; ++j) {
float distance2 = glm::distance2(_views[j].center, glm::vec3(_proxies[i].sphere));
for (uint8_t c = 0; c < region; ++c) {
float touchDistance = _views[j].radiuses[c] + _proxies[i].sphere.w;
if (distance2 < touchDistance * touchDistance) {
region = c;
break;
}
}
}
proxy.prevRegion = proxy.region;
proxy.region = region;
if (proxy.region != proxy.prevRegion) {
changes.emplace_back(Space::Change((int32_t)i, proxy.region, proxy.prevRegion));
}
}
}
}

View file

@ -0,0 +1,79 @@
//
// Space.h
// libraries/workload/src/workload
//
// Created by Andrew Meadows 2018.01.30
// Copyright 2018 High Fidelity, Inc.
//
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
// Simple plane class.
//
// 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_workload_Space_h
#define hifi_workload_Space_h
#include <vector>
#include <glm/glm.hpp>
namespace workload {
class Space {
public:
static const uint8_t REGION_NEAR = 0;
static const uint8_t REGION_MIDDLE = 1;
static const uint8_t REGION_FAR = 2;
static const uint8_t REGION_UNKNOWN = 3;
static const uint8_t REGION_INVALID = 4;
using Sphere = glm::vec4; // <x,y,z> = center, w = radius
class Proxy {
public:
Proxy(const Sphere& s) : sphere(s) {}
Sphere sphere;
uint8_t region { REGION_UNKNOWN };
uint8_t prevRegion { REGION_UNKNOWN };
};
class View {
public:
View(const glm::vec3& pos, float nearRadius, float midRadius, float farRadius) : center(pos) {
radiuses[0] = nearRadius;
radiuses[1] = midRadius;
radiuses[2] = farRadius;
}
glm::vec3 center { 0.0f, 0.0f, 0.0f };
float radiuses[3] { 0.0f, 0.0f, 0.0f };
};
class Change {
public:
Change(int32_t i, uint32_t c, uint32_t p) : proxyId(i), region(c), prevRegion(p) {}
int32_t proxyId { -1 };
uint8_t region { 0 };
uint8_t prevRegion { 0 };
};
Space() {}
int32_t createProxy(const Sphere& sphere);
void deleteProxy(int32_t proxyId);
void updateProxy(int32_t proxyId, const Sphere& sphere);
void setViews(const std::vector<View>& views);
uint32_t getNumObjects() const { return (uint32_t)(_proxies.size() - _freeIndices.size()); }
void categorizeAndGetChanges(std::vector<Change>& changes);
private:
std::vector<Proxy> _proxies;
std::vector<View> _views;
std::vector<int32_t> _freeIndices;
};
} // namespace workload
#endif // hifi_workload_Space_h

View file

@ -19,7 +19,7 @@ if (WIN32 AND (NOT USE_GLES))
set(TARGET_NAME oculus)
setup_hifi_plugin(Multimedia)
link_hifi_libraries(
shared gl gpu gpu-gl controllers ui qml
shared task gl gpu gpu-gl controllers ui qml
plugins ui-plugins display-plugins input-plugins
audio-client networking render-utils
${PLATFORM_GL_BACKEND}

View file

@ -11,7 +11,7 @@ if (WIN32 AND (NOT USE_GLES))
add_definitions(-DGLEW_STATIC)
set(TARGET_NAME openvr)
setup_hifi_plugin(Gui Qml Multimedia)
link_hifi_libraries(shared gl qml networking controllers ui
link_hifi_libraries(shared task gl qml networking controllers ui
plugins display-plugins ui-plugins input-plugins script-engine
audio-client render-utils graphics gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND})

View file

@ -5,7 +5,7 @@ setup_hifi_project(Quick Gui Script)
setup_memory_debugger()
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
link_hifi_libraries(
shared networking gl
shared task networking gl
ktx gpu procedural octree image
graphics model-networking fbx animation
script-engine render render-utils

View file

@ -13,7 +13,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(
shared networking animation
shared task networking animation
ktx image octree gl gpu gpu-gl
render render-utils
graphics fbx model-networking

View file

@ -13,7 +13,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(
shared networking octree
shared task networking octree
gl gpu render ktx image animation
graphics fbx model-networking
render-utils

View file

@ -0,0 +1,8 @@
# Declare dependencies
macro (setup_testcase_dependencies)
link_hifi_libraries(shared workload)
package_libraries_for_deployment()
endmacro ()
setup_hifi_testcase()

View file

@ -0,0 +1,272 @@
//
// SpaceTests.cpp
// tests/physics/src
//
// Created by Andrew Meadows on 2017.01.26
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SpaceTests.h"
#include <iostream>
#include <workload/Space.h>
#include <StreamUtils.h>
#include <SharedUtil.h>
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
QTEST_MAIN(SpaceTests)
void SpaceTests::testOverlaps() {
workload::Space space;
using Changes = std::vector<workload::Space::Change>;
using Views = std::vector<workload::Space::View>;
glm::vec3 viewCenter(0.0f, 0.0f, 0.0f);
float near = 1.0f;
float mid = 2.0f;
float far = 3.0f;
Views views;
views.push_back(workload::Space::View(viewCenter, near, mid, far));
space.setViews(views);
int32_t proxyId = 0;
const float DELTA = 0.001f;
float proxyRadius = 0.5f;
glm::vec3 proxyPosition = viewCenter + glm::vec3(0.0f, 0.0f, far + proxyRadius + DELTA);
workload::Space::Sphere proxySphere(proxyPosition, proxyRadius);
{ // create very_far proxy
proxyId = space.createProxy(proxySphere);
QVERIFY(space.getNumObjects() == 1);
Changes changes;
space.categorizeAndGetChanges(changes);
QVERIFY(changes.size() == 0);
}
{ // move proxy far
float newRadius = 1.0f;
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, far + newRadius - DELTA);
workload::Space::Sphere newSphere(newPosition, newRadius);
space.updateProxy(proxyId, newSphere);
Changes changes;
space.categorizeAndGetChanges(changes);
QVERIFY(changes.size() == 1);
QVERIFY(changes[0].proxyId == proxyId);
QVERIFY(changes[0].region == workload::Space::REGION_FAR);
QVERIFY(changes[0].prevRegion == workload::Space::REGION_UNKNOWN);
}
{ // move proxy mid
float newRadius = 1.0f;
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, mid + newRadius - DELTA);
workload::Space::Sphere newSphere(newPosition, newRadius);
space.updateProxy(proxyId, newSphere);
Changes changes;
space.categorizeAndGetChanges(changes);
QVERIFY(changes.size() == 1);
QVERIFY(changes[0].proxyId == proxyId);
QVERIFY(changes[0].region == workload::Space::REGION_MIDDLE);
QVERIFY(changes[0].prevRegion == workload::Space::REGION_FAR);
}
{ // move proxy near
float newRadius = 1.0f;
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, near + newRadius - DELTA);
workload::Space::Sphere newSphere(newPosition, newRadius);
space.updateProxy(proxyId, newSphere);
Changes changes;
space.categorizeAndGetChanges(changes);
QVERIFY(changes.size() == 1);
QVERIFY(changes[0].proxyId == proxyId);
QVERIFY(changes[0].region == workload::Space::REGION_NEAR);
QVERIFY(changes[0].prevRegion == workload::Space::REGION_MIDDLE);
}
{ // delete proxy
// NOTE: atm deleting a proxy doesn't result in a "Change"
space.deleteProxy(proxyId);
Changes changes;
space.categorizeAndGetChanges(changes);
QVERIFY(changes.size() == 0);
QVERIFY(space.getNumObjects() == 0);
}
}
#ifdef MANUAL_TEST
const float WORLD_WIDTH = 1000.0f;
const float MIN_RADIUS = 1.0f;
const float MAX_RADIUS = 100.0f;
float randomFloat() {
return 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f;
}
glm::vec3 randomVec3() {
glm::vec3 v(randomFloat(), randomFloat(), randomFloat());
return v;
}
void generateSpheres(uint32_t numProxies, std::vector<workload::Space::Sphere>& spheres) {
spheres.reserve(numProxies);
for (uint32_t i = 0; i < numProxies; ++i) {
workload::Space::Sphere sphere(
WORLD_WIDTH * randomFloat(),
WORLD_WIDTH * randomFloat(),
WORLD_WIDTH * randomFloat(),
(MIN_RADIUS + (MAX_RADIUS - MIN_RADIUS)) * randomFloat());
spheres.push_back(sphere);
}
}
void generatePositions(uint32_t numProxies, std::vector<glm::vec3>& positions) {
positions.reserve(numProxies);
for (uint32_t i = 0; i < numProxies; ++i) {
positions.push_back(WORLD_WIDTH * randomVec3());
}
}
void generateRadiuses(uint32_t numRadiuses, std::vector<float>& radiuses) {
radiuses.reserve(numRadiuses);
for (uint32_t i = 0; i < numRadiuses; ++i) {
radiuses.push_back(MIN_RADIUS + (MAX_RADIUS - MIN_RADIUS) * randomFloat());
}
}
void SpaceTests::benchmark() {
uint32_t numProxies[] = { 100, 1000, 10000, 100000 };
uint32_t numTests = 4;
std::vector<uint64_t> timeToAddAll;
std::vector<uint64_t> timeToRemoveAll;
std::vector<uint64_t> timeToMoveView;
std::vector<uint64_t> timeToMoveProxies;
for (uint32_t i = 0; i < numTests; ++i) {
workload::Space space;
{ // build the views
std::vector<glm::vec3> viewPositions;
viewPositions.push_back(glm::vec3(0.0f, 0.0f, 0.0f));
viewPositions.push_back(glm::vec3(0.0f, 0.0f, 0.1f * WORLD_WIDTH));
float radius0 = 0.25f * WORLD_WIDTH;
float radius1 = 0.50f * WORLD_WIDTH;
float radius2 = 0.75f * WORLD_WIDTH;
std::vector<workload::Space::View> views;
views.push_back(workload::Space::View(viewPositions[0], radius0, radius1, radius2));
views.push_back(workload::Space::View(viewPositions[1], radius0, radius1, radius2));
space.setViews(views);
}
// build the proxies
uint32_t n = numProxies[i];
std::vector<workload::Space::Sphere> proxySpheres;
generateSpheres(n, proxySpheres);
std::vector<int32_t> proxyKeys;
proxyKeys.reserve(n);
// measure time to put proxies in the space
uint64_t startTime = usecTimestampNow();
for (uint32_t j = 0; j < n; ++j) {
int32_t key = space.createProxy(proxySpheres[j]);
proxyKeys.push_back(key);
}
uint64_t usec = usecTimestampNow() - startTime;
timeToAddAll.push_back(usec);
{
// build the views
std::vector<glm::vec3> viewPositions;
viewPositions.push_back(glm::vec3(1.0f, 2.0f, 3.0f));
viewPositions.push_back(glm::vec3(1.0f, 2.0f, 3.0f + 0.1f * WORLD_WIDTH));
float radius0 = 0.25f * WORLD_WIDTH;
float radius1 = 0.50f * WORLD_WIDTH;
float radius2 = 0.75f * WORLD_WIDTH;
std::vector<workload::Space::View> views;
views.push_back(workload::Space::View(viewPositions[0], radius0, radius1, radius2));
views.push_back(workload::Space::View(viewPositions[1], radius0, radius1, radius2));
space.setViews(views);
}
// measure time to categorizeAndGetChanges everything
std::vector<workload::Space::Change> changes;
startTime = usecTimestampNow();
space.categorizeAndGetChanges(changes);
usec = usecTimestampNow() - startTime;
timeToMoveView.push_back(usec);
// move every 10th proxy around
const float proxySpeed = 1.0f;
std::vector<workload::Space::Sphere> newSpheres;
uint32_t numMovingProxies = n / 10;
uint32_t jstep = n / numMovingProxies;
uint32_t maxJ = numMovingProxies * jstep - 1;
glm::vec3 direction;
for (uint32_t j = 0; j < maxJ - jstep; j += jstep) {
glm::vec3 position = (glm::vec3)proxySpheres[j];
glm::vec3 destination = (glm::vec3)proxySpheres[j + jstep];
direction = glm::normalize(destination - position);
glm::vec3 newPosition = position + proxySpeed * direction;
newSpheres.push_back(workload::Space::Sphere(newPosition, proxySpheres[j].w));
}
glm::vec3 position = (glm::vec3)proxySpheres[maxJ = jstep];
glm::vec3 destination = (glm::vec3)proxySpheres[0];
direction = glm::normalize(destination - position);
direction = position + proxySpeed * direction;
newSpheres.push_back(workload::Space::Sphere(direction, proxySpheres[0].w));
uint32_t k = 0;
startTime = usecTimestampNow();
for (uint32_t j = 0; j < maxJ; j += jstep) {
space.updateProxy(proxyKeys[j], newSpheres[k++]);
}
changes.clear();
space.categorizeAndGetChanges(changes);
usec = usecTimestampNow() - startTime;
timeToMoveProxies.push_back(usec);
// measure time to remove proxies from space
startTime = usecTimestampNow();
for (uint32_t j = 0; j < n; ++j) {
space.deleteProxy(proxyKeys[j]);
}
usec = usecTimestampNow() - startTime;
timeToRemoveAll.push_back(usec);
}
std::cout << "[numProxies, timeToAddAll] = [" << std::endl;
for (uint32_t i = 0; i < timeToAddAll.size(); ++i) {
uint32_t n = numProxies[i];
std::cout << " " << n << ", " << timeToAddAll[i] << std::endl;
}
std::cout << "];" << std::endl;
std::cout << "[numProxies, timeToMoveView] = [" << std::endl;
for (uint32_t i = 0; i < timeToMoveView.size(); ++i) {
uint32_t n = numProxies[i];
std::cout << " " << n << ", " << timeToMoveView[i] << std::endl;
}
std::cout << "];" << std::endl;
std::cout << "[numProxies, timeToMoveProxies] = [" << std::endl;
for (uint32_t i = 0; i < timeToMoveProxies.size(); ++i) {
uint32_t n = numProxies[i];
std::cout << " " << n << "/10, " << timeToMoveProxies[i] << std::endl;
}
std::cout << "];" << std::endl;
std::cout << "[numProxies, timeToRemoveAll] = [" << std::endl;
for (uint32_t i = 0; i < timeToRemoveAll.size(); ++i) {
uint32_t n = numProxies[i];
std::cout << " " << n << ", " << timeToRemoveAll[i] << std::endl;
}
std::cout << "];" << std::endl;
}
#endif // MANUAL_TEST

View file

@ -0,0 +1,29 @@
//
// SpaceTests.h
// tests/physics/src
//
// Created by Andrew Meadows on 2017.01.26
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_workload_SpaceTests_h
#define hifi_workload_SpaceTests_h
#include <QtTest/QtTest>
//#define MANUAL_TEST
class SpaceTests : public QObject {
Q_OBJECT
private slots:
void testOverlaps();
#ifdef MANUAL_TEST
void benchmark();
#endif // MANUAL_TEST
};
#endif // hifi_workload_SpaceTests_h