From ea9dee1286f5fcd361296ecccd02f705f33be145 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 12 Mar 2018 17:49:45 -0700 Subject: [PATCH 1/4] FIrst step introducing the Transaction model to access the Proxy of the space --- libraries/workload/src/workload/Proxy.h | 38 +++ libraries/workload/src/workload/Space.h | 49 ++-- .../workload/src/workload/Transaction.cpp | 235 ++++++++++++++++++ libraries/workload/src/workload/Transaction.h | 166 +++++++++++++ 4 files changed, 468 insertions(+), 20 deletions(-) create mode 100644 libraries/workload/src/workload/Proxy.h create mode 100644 libraries/workload/src/workload/Transaction.cpp create mode 100644 libraries/workload/src/workload/Transaction.h diff --git a/libraries/workload/src/workload/Proxy.h b/libraries/workload/src/workload/Proxy.h new file mode 100644 index 0000000000..bf3de6e4c9 --- /dev/null +++ b/libraries/workload/src/workload/Proxy.h @@ -0,0 +1,38 @@ +// +// Proxy.h +// libraries/workload/src/workload +// +// Created by Andrew Meadows 2018.01.30 +// Copyright 2018 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_Proxy_h +#define hifi_workload_Proxy_h + +#include "View.h" + +namespace workload { + +using Index = int32_t; +using ProxyID = Index; + +using IndexVector = std::vector; + +class Proxy { +public: + Proxy() : sphere(0.0f) {} + Proxy(const Sphere& s) : sphere(s) {} + + Sphere sphere; + uint8_t region{ Region::UNKNOWN }; + uint8_t prevRegion{ Region::UNKNOWN }; + uint16_t _padding; + uint32_t _paddings[3]; +}; + + +} // namespace workload + +#endif // hifi_workload_Proxy_h diff --git a/libraries/workload/src/workload/Space.h b/libraries/workload/src/workload/Space.h index 0acd69194b..699d44f7a1 100644 --- a/libraries/workload/src/workload/Space.h +++ b/libraries/workload/src/workload/Space.h @@ -19,29 +19,14 @@ #include #include -#include "View.h" - +#include "Transaction.h" namespace workload { -using IndexVector = std::vector; - class Space { public: - using Sphere = glm::vec4; // = center, w = radius using ProxyUpdate = std::pair; - class Proxy { - public: - Proxy() : sphere(0.0f) {} - Proxy(const Sphere& s) : sphere(s) {} - Sphere sphere; - uint8_t region { Region::UNKNOWN }; - uint8_t prevRegion { Region::UNKNOWN }; - uint16_t _padding; - uint32_t _paddings[3]; - }; - class Change { public: Change(int32_t i, uint32_t c, uint32_t p) : proxyId(i), region(c), prevRegion(p) {} @@ -52,10 +37,27 @@ public: Space() {} - void clear(); - int32_t createProxy(const Sphere& sphere); - void deleteProxies(const IndexVector& deadIndices); - void updateProxies(const std::vector& changedProxies); + // This call is thread safe, can be called from anywhere to allocate a new ID + ProxyID allocateID(); + + // Check that the ID is valid and allocated for this space, this a threadsafe call + bool isAllocatedID(const ProxyID& id) const; + + // THis is the total number of allocated proxies, this a threadsafe call + Index getNumAllocatedProxies() const { return _numAllocatedProxies.load(); } + + // Enqueue transaction to the space + void enqueueTransaction(const Transaction& transaction); + + // Enqueue transaction to the space + void enqueueTransaction(Transaction&& transaction); + + // Enqueue end of frame transactions boundary + uint32_t enqueueFrame(); + + // Process the pending transactions queued + void processTransactionQueue(); + void setViews(const std::vector& views); uint32_t getNumViews() const { return (uint32_t)(_views.size()); } @@ -69,6 +71,13 @@ public: uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const; private: + + + void clear(); + int32_t createProxy(const Sphere& sphere); + void deleteProxies(const IndexVector& deadIndices); + void updateProxies(const std::vector& changedProxies); + void deleteProxy(int32_t proxyId); void updateProxy(int32_t proxyId, const Sphere& sphere); diff --git a/libraries/workload/src/workload/Transaction.cpp b/libraries/workload/src/workload/Transaction.cpp new file mode 100644 index 0000000000..3a5e0b31d8 --- /dev/null +++ b/libraries/workload/src/workload/Transaction.cpp @@ -0,0 +1,235 @@ +// +// Transaction.cpp +// libraries/workload/src/workload +// +// Created by Sam Gateau 2018.03.12 +// Copyright 2018 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 "Transaction.h" + +using namespace workload; + + +void Transaction::resetItem(ItemID id, PayloadPointer& payload) { + if (payload) { + _resetItems.emplace_back(Reset{ id, payload }); + } else { + qCDebug(renderlogging) << "WARNING: Transaction::resetItem with a null payload!"; + removeItem(id); + } +} + +void Transaction::removeItem(ItemID id) { + _removedItems.emplace_back(id); +} + +void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) { + _updatedItems.emplace_back(id, functor); +} + +void Transaction::reserve(const std::vector& transactionContainer) { + size_t resetItemsCount = 0; + size_t removedItemsCount = 0; + size_t updatedItemsCount = 0; + + for (const auto& transaction : transactionContainer) { + resetItemsCount += transaction._resetItems.size(); + removedItemsCount += transaction._removedItems.size(); + updatedItemsCount += transaction._updatedItems.size(); + } + + _resetItems.reserve(resetItemsCount); + _removedItems.reserve(removedItemsCount); + _updatedItems.reserve(updatedItemsCount); +} + +void Transaction::merge(const std::vector& transactionContainer) { + reserve(transactionContainer); + for (const auto& transaction : transactionContainer) { + merge(transaction); + } +} + + +void Transaction::merge(std::vector&& transactionContainer) { + reserve(transactionContainer); + auto begin = std::make_move_iterator(transactionContainer.begin()); + auto end = std::make_move_iterator(transactionContainer.end()); + for (auto itr = begin; itr != end; ++itr) { + merge(*itr); + } + transactionContainer.clear(); +} + + +template +void moveElements(T& target, T& source) { + target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.end())); + source.clear(); +} + +template +void copyElements(T& target, const T& source) { + target.insert(target.end(), source.begin(), source.end()); +} + + +void Transaction::merge(Transaction&& transaction) { + moveElements(_resetItems, transaction._resetItems); + moveElements(_removedItems, transaction._removedItems); + moveElements(_updatedItems, transaction._updatedItems); +} + +void Transaction::merge(const Transaction& transaction) { + copyElements(_resetItems, transaction._resetItems); + copyElements(_removedItems, transaction._removedItems); + copyElements(_updatedItems, transaction._updatedItems); +} + +void Transaction::clear() { + _resetItems.clear(); + _removedItems.clear(); + _updatedItems.clear(); +} + + + + +Collection::Collection() { + _items.push_back(Item()); // add the itemID #0 to nothing +} + +Collection::~Collection() { + qCDebug(renderlogging) << "Scene::~Scene()"; +} + +ItemID Collection::allocateID() { + // Just increment and return the proevious value initialized at 0 + return _IDAllocator.fetch_add(1); +} + +bool Collection::isAllocatedID(const ItemID& id) const { + return Item::isValidID(id) && (id < _numAllocatedItems.load()); +} + +/// Enqueue change batch to the scene +void Collection::enqueueTransaction(const Transaction& transaction) { + std::unique_lock lock(_transactionQueueMutex); + _transactionQueue.emplace_back(transaction); +} + +void Collection::enqueueTransaction(Transaction&& transaction) { + std::unique_lock lock(_transactionQueueMutex); + _transactionQueue.emplace_back(std::move(transaction)); +} + +uint32_t Collection::enqueueFrame() { + PROFILE_RANGE(render, __FUNCTION__); + TransactionQueue localTransactionQueue; + { + std::unique_lock lock(_transactionQueueMutex); + localTransactionQueue.swap(_transactionQueue); + } + + Transaction consolidatedTransaction; + consolidatedTransaction.merge(std::move(localTransactionQueue)); + { + std::unique_lock lock(_transactionFramesMutex); + _transactionFrames.push_back(consolidatedTransaction); + } + + return ++_transactionFrameNumber; +} + + +void Collection::processTransactionQueue() { + PROFILE_RANGE(render, __FUNCTION__); + + static TransactionFrames queuedFrames; + { + // capture the queued frames and clear the queue + std::unique_lock lock(_transactionFramesMutex); + queuedFrames.swap(_transactionFrames); + } + + // go through the queue of frames and process them + for (auto& frame : queuedFrames) { + processTransactionFrame(frame); + } + + queuedFrames.clear(); +} + +void Collection::processTransactionFrame(const Transaction& transaction) { + PROFILE_RANGE(render, __FUNCTION__); + { + std::unique_lock lock(_itemsMutex); + // Here we should be able to check the value of last ItemID allocated + // and allocate new items accordingly + ItemID maxID = _IDAllocator.load(); + if (maxID > _items.size()) { + _items.resize(maxID + 100); // allocate the maxId and more + } + // Now we know for sure that we have enough items in the array to + // capture anything coming from the transaction + + // resets and potential NEW items + resetItems(transaction._resetItems); + + // Update the numItemsAtomic counter AFTER the reset changes went through + _numAllocatedItems.exchange(maxID); + + // updates + updateItems(transaction._updatedItems); + + // removes + removeItems(transaction._removedItems); + + // add transitions + transitionItems(transaction._addedTransitions); + reApplyTransitions(transaction._reAppliedTransitions); + queryTransitionItems(transaction._queriedTransitions); + + // Update the numItemsAtomic counter AFTER the pending changes went through + _numAllocatedItems.exchange(maxID); + } +} + +void Collection::resetItems(const Transaction::Resets& transactions) { + for (auto& reset : transactions) { + // Access the true item + auto itemId = std::get<0>(reset); + auto& item = _items[itemId]; + + // Reset the item with a new payload + item.resetPayload(std::get<1>(reset)); + } +} + +void Collection::removeItems(const Transaction::Removes& transactions) { + for (auto removedID : transactions) { + // Access the true item + auto& item = _items[removedID]; + + // Kill it + item.kill(); + } +} + +void Collection::updateItems(const Transaction::Updates& transactions) { + for (auto& update : transactions) { + auto updateID = std::get<0>(update); + if (updateID == Item::INVALID_ITEM_ID) { + continue; + } + + // Access the true item + auto& item = _items[updateID]; + + // Update the item + item.update(std::get<1>(update)); + } +} \ No newline at end of file diff --git a/libraries/workload/src/workload/Transaction.h b/libraries/workload/src/workload/Transaction.h new file mode 100644 index 0000000000..87bf0e4209 --- /dev/null +++ b/libraries/workload/src/workload/Transaction.h @@ -0,0 +1,166 @@ +// +// Transaction.h +// libraries/workload/src/workload +// +// Created by Sam Gateau 2018.03.12 +// Copyright 2018 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_Transaction_h +#define hifi_workload_Transaction_h + +#include +#include +#include + +#include "Proxy.h" + + +namespace workload { + +// Transaction is the mechanism to make any change to the Space. +// Whenever a new proxy need to be reset, +// or when an proxy changes its position or its size +// or when an proxy is destroyed +// These changes must be expressed through the corresponding command from the Transaction +// The Transaction is then queued on the Space so all the pending transactions can be consolidated and processed at the time +// of updating the space at the Frame boundary. +// +class Transaction { + friend class Space; +public: + using ProxyPayload = Sphere; + + Transaction() {} + ~Transaction() {} + + // Proxy transactions + void reset(ProxyID id, const ProxyPayload& sphere); + void remove(ProxyID id); + bool hasRemovals() const { return !_removedItems.empty(); } + + void update(ProxyID id, const ProxyPayload& sphere); + + void reserve(const std::vector& transactionContainer); + void merge(const std::vector& transactionContainer); + void merge(std::vector&& transactionContainer); + void merge(const Transaction& transaction); + void merge(Transaction&& transaction); + void clear(); + +protected: + + using Reset = std::tuple; + using Remove = ProxyID; + using Update = std::tuple; + + using Resets = std::vector; + using Removes = std::vector; + using Updates = std::vector; + + Resets _resetItems; + Removes _removedItems; + Updates _updatedItems; +}; +typedef std::vector TransactionQueue; + +namespace indexed_container { + + using Index = int32_t; + const Index MAXIMUM_INDEX{ 1 << 30 }; + const Index INVALID_INDEX{ -1 }; + using Indices = std::vector< Index >; + + template + class Allocator { + public: + Allocator() {} + Indices _freeIndices; + std::atomic _nextNewIndex{ 0 }; + std::atomic _numFreeIndices{ 0 }; + + bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex.load())); } + Index getNumIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); } + Index getNumFreeIndices() const { return (Index)_freeIndices.size(); } + Index getNumAllocatedIndices() const { return _nextNewIndex.load(); } + + Index allocateIndex() { + if (_freeIndices.empty()) { + Index index = _nextNewIndex; + if (index >= MaxNumElements) { + // abort! we are trying to go overboard with the total number of allocated elements + assert(false); + // This should never happen because Bricks are allocated along with the cells and there + // is already a cap on the cells allocation + return INVALID_INDEX; + } + _nextNewIndex++; + return index; + } else { + Index index = _freeIndices.back(); + _freeIndices.pop_back(); + return index; + } + } + + void freeIndex(Index index) { + if (checkIndex(index)) { + _freeIndices.push_back(index); + } + } + + void clear() { + _freeIndices.clear(); + _nextNewIndex = 0; + } + }; +} + +class Collection { +public: + + // This call is thread safe, can be called from anywhere to allocate a new ID + ProxyID allocateID(); + + // Check that the ID is valid and allocated for this space, this a threadsafe call + bool isAllocatedID(const ProxyID& id) const; + + // THis is the total number of allocated proxies, this a threadsafe call + Index getNumAllocatedProxies() const { return _numAllocatedProxies.load(); } + + // Enqueue transaction to the space + void enqueueTransaction(const Transaction& transaction); + + // Enqueue transaction to the space + void enqueueTransaction(Transaction&& transaction); + + // Enqueue end of frame transactions boundary + uint32_t enqueueFrame(); + + // Process the pending transactions queued + void processTransactionQueue(); + +protected: + + // Thread safe elements that can be accessed from anywhere + std::atomic _IDAllocator{ 1 }; // first valid itemID will be One + std::atomic _numAllocatedItems{ 1 }; // num of allocated items, matching the _items.size() + std::mutex _transactionQueueMutex; + TransactionQueue _transactionQueue; + + + std::mutex _transactionFramesMutex; + using TransactionFrames = std::vector; + TransactionFrames _transactionFrames; + uint32_t _transactionFrameNumber{ 0 }; + + // Process one transaction frame + void processTransactionFrame(const Transaction& transaction); +}; + +} // namespace workload + +#endif // hifi_workload_Transaction_h \ No newline at end of file From 1af139a4d42f6d5aa8f0f575029aebdd7d68da0d Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 12 Mar 2018 18:01:22 -0700 Subject: [PATCH 2/4] FIrst step introducing the Transaction model to access the Proxy of the space --- libraries/workload/src/workload/Proxy.h | 5 - libraries/workload/src/workload/Transaction.h | 120 ++++++++++-------- 2 files changed, 65 insertions(+), 60 deletions(-) diff --git a/libraries/workload/src/workload/Proxy.h b/libraries/workload/src/workload/Proxy.h index bf3de6e4c9..b7f8094b00 100644 --- a/libraries/workload/src/workload/Proxy.h +++ b/libraries/workload/src/workload/Proxy.h @@ -15,11 +15,6 @@ namespace workload { -using Index = int32_t; -using ProxyID = Index; - -using IndexVector = std::vector; - class Proxy { public: Proxy() : sphere(0.0f) {} diff --git a/libraries/workload/src/workload/Transaction.h b/libraries/workload/src/workload/Transaction.h index 87bf0e4209..098e953f36 100644 --- a/libraries/workload/src/workload/Transaction.h +++ b/libraries/workload/src/workload/Transaction.h @@ -12,6 +12,8 @@ #ifndef hifi_workload_Transaction_h #define hifi_workload_Transaction_h +#include +#include #include #include #include @@ -21,6 +23,64 @@ namespace workload { + namespace indexed_container { + + using Index = int32_t; + const Index MAXIMUM_INDEX{ 1 << 30 }; + const Index INVALID_INDEX{ -1 }; + using Indices = std::vector< Index >; + + template + class Allocator { + public: + Allocator() {} + Indices _freeIndices; + Index _nextNewIndex{ 0 }; + + bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex)); } + Index getNumIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); } + Index getNumFreeIndices() const { return (Index)_freeIndices.size(); } + Index getNumAllocatedIndices() const { return _nextNewIndex; } + + Index allocateIndex() { + if (_freeIndices.empty()) { + Index index = _nextNewIndex; + if (index >= MaxNumElements) { + // abort! we are trying to go overboard with the total number of allocated elements + assert(false); + // This should never happen because Bricks are allocated along with the cells and there + // is already a cap on the cells allocation + return INVALID_INDEX; + } + _nextNewIndex++; + return index; + } else { + Index index = _freeIndices.back(); + _freeIndices.pop_back(); + return index; + } + } + + void freeIndex(Index index) { + if (checkIndex(index)) { + _freeIndices.push_back(index); + } + } + + void clear() { + _freeIndices.clear(); + _nextNewIndex = 0; + } + }; + } + + + using Index = indexed_container::Index; + using IndexVector = indexed_container::Indices; + + using ProxyID = Index; + + // Transaction is the mechanism to make any change to the Space. // Whenever a new proxy need to be reset, // or when an proxy changes its position or its size @@ -67,58 +127,6 @@ protected: }; typedef std::vector TransactionQueue; -namespace indexed_container { - - using Index = int32_t; - const Index MAXIMUM_INDEX{ 1 << 30 }; - const Index INVALID_INDEX{ -1 }; - using Indices = std::vector< Index >; - - template - class Allocator { - public: - Allocator() {} - Indices _freeIndices; - std::atomic _nextNewIndex{ 0 }; - std::atomic _numFreeIndices{ 0 }; - - bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex.load())); } - Index getNumIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); } - Index getNumFreeIndices() const { return (Index)_freeIndices.size(); } - Index getNumAllocatedIndices() const { return _nextNewIndex.load(); } - - Index allocateIndex() { - if (_freeIndices.empty()) { - Index index = _nextNewIndex; - if (index >= MaxNumElements) { - // abort! we are trying to go overboard with the total number of allocated elements - assert(false); - // This should never happen because Bricks are allocated along with the cells and there - // is already a cap on the cells allocation - return INVALID_INDEX; - } - _nextNewIndex++; - return index; - } else { - Index index = _freeIndices.back(); - _freeIndices.pop_back(); - return index; - } - } - - void freeIndex(Index index) { - if (checkIndex(index)) { - _freeIndices.push_back(index); - } - } - - void clear() { - _freeIndices.clear(); - _nextNewIndex = 0; - } - }; -} - class Collection { public: @@ -129,7 +137,7 @@ public: bool isAllocatedID(const ProxyID& id) const; // THis is the total number of allocated proxies, this a threadsafe call - Index getNumAllocatedProxies() const { return _numAllocatedProxies.load(); } + Index getNumAllocatedProxies() const { return _IDAllocator.getNumAllocatedIndices(); } // Enqueue transaction to the space void enqueueTransaction(const Transaction& transaction); @@ -146,8 +154,10 @@ public: protected: // Thread safe elements that can be accessed from anywhere - std::atomic _IDAllocator{ 1 }; // first valid itemID will be One - std::atomic _numAllocatedItems{ 1 }; // num of allocated items, matching the _items.size() + indexed_container::Allocator<> _IDAllocator; + + //std::atomic _IDAllocator{ 1 }; // first valid itemID will be One + //std::atomic _numAllocatedItems{ 1 }; // num of allocated items, matching the _items.size() std::mutex _transactionQueueMutex; TransactionQueue _transactionQueue; From a2993c7cdb7bda77b60bf9cc7adc39452b241a51 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 13 Mar 2018 01:45:21 -0700 Subject: [PATCH 3/4] First version wof space working with Transaction & Collection --- .../src/workload/GameWorkloadRenderer.cpp | 6 +- interface/src/workload/GameWorkloadRenderer.h | 4 +- .../src/EntityTreeRenderer.cpp | 15 ++- .../src/EntityTreeRenderer.h | 2 +- libraries/workload/src/workload/Engine.cpp | 11 ++ libraries/workload/src/workload/Engine.h | 18 +++ libraries/workload/src/workload/Proxy.h | 6 +- libraries/workload/src/workload/Space.cpp | 116 +++++++++++++++--- libraries/workload/src/workload/Space.h | 55 +++------ .../workload/src/workload/Transaction.cpp | 86 ++++++------- libraries/workload/src/workload/Transaction.h | 31 +++-- 11 files changed, 229 insertions(+), 121 deletions(-) diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index 8509405eb0..c21f93d764 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -56,7 +56,7 @@ void GameSpaceToRender::run(const workload::WorkloadContextPointer& runContext, return; } - std::vector proxies(space->getNumAllocatedProxies()); + workload::Proxy::Vector proxies(space->getNumAllocatedProxies()); space->copyProxyValues(proxies.data(), (uint32_t)proxies.size()); workload::Views views(space->getNumViews()); @@ -130,9 +130,9 @@ void GameWorkloadRenderItem::showViews(bool show) { } -void GameWorkloadRenderItem::setAllProxies(const std::vector& proxies) { +void GameWorkloadRenderItem::setAllProxies(const workload::Proxy::Vector& proxies) { _myOwnProxies = proxies; - static const uint32_t sizeOfProxy = sizeof(workload::Space::Proxy); + static const uint32_t sizeOfProxy = sizeof(workload::Proxy); if (!_allProxiesBuffer) { _allProxiesBuffer = std::make_shared(sizeOfProxy); } diff --git a/interface/src/workload/GameWorkloadRenderer.h b/interface/src/workload/GameWorkloadRenderer.h index 7a0800d40c..36cb32f152 100644 --- a/interface/src/workload/GameWorkloadRenderer.h +++ b/interface/src/workload/GameWorkloadRenderer.h @@ -63,7 +63,7 @@ public: void showProxies(bool show); void showViews(bool show); - void setAllProxies(const std::vector& proxies); + void setAllProxies(const workload::Proxy::Vector& proxies); void setAllViews(const workload::Views& views); render::ItemKey getKey() const; @@ -71,7 +71,7 @@ public: protected: render::Item::Bound _bound; - std::vector _myOwnProxies; + workload::Proxy::Vector _myOwnProxies; gpu::BufferPointer _allProxiesBuffer; uint32_t _numAllProxies{ 0 }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 04e8c9a414..dfb888f9cb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -281,8 +281,11 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r } if (entity->getSpaceIndex() == -1) { std::unique_lock lock(_spaceLock); - workload::Space::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius()); - int32_t spaceIndex = _space->createProxy(sphere); + auto spaceIndex = _space->allocateID(); + workload::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius()); + workload::Transaction transaction; + transaction.reset(spaceIndex, sphere); + _space->enqueueTransaction(transaction); entity->setSpaceIndex(spaceIndex); connect(entity.get(), &EntityItem::spaceUpdate, this, &EntityTreeRenderer::handleSpaceUpdate, Qt::QueuedConnection); } @@ -427,17 +430,19 @@ void EntityTreeRenderer::update(bool simulate) { scene->enqueueTransaction(transaction); } } + workload::Transaction spaceTransaction; { // update proxies in the workload::Space std::unique_lock lock(_spaceLock); - _space->updateProxies(_spaceUpdates); + spaceTransaction.update(_spaceUpdates); _spaceUpdates.clear(); } { std::vector staleProxies; tree->swapStaleProxies(staleProxies); + spaceTransaction.remove(staleProxies); { std::unique_lock lock(_spaceLock); - _space->deleteProxies(staleProxies); + _space->enqueueTransaction(spaceTransaction); } } @@ -458,7 +463,7 @@ void EntityTreeRenderer::update(bool simulate) { void EntityTreeRenderer::handleSpaceUpdate(std::pair proxyUpdate) { std::unique_lock lock(_spaceLock); - _spaceUpdates.push_back(proxyUpdate); + _spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second); } bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 5840dc6832..fa9c2ac6d2 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -270,7 +270,7 @@ private: mutable std::mutex _spaceLock; workload::SpacePointer _space{ new workload::Space() }; - std::vector _spaceUpdates; + workload::Transaction::Updates _spaceUpdates; }; diff --git a/libraries/workload/src/workload/Engine.cpp b/libraries/workload/src/workload/Engine.cpp index 525872f190..0bb9c73fa0 100644 --- a/libraries/workload/src/workload/Engine.cpp +++ b/libraries/workload/src/workload/Engine.cpp @@ -29,6 +29,7 @@ namespace workload { using JobModel = Task::ModelI; void build(JobModel& model, const Varying& in, Varying& out) { model.addJob("setupViews", in); + model.addJob("updateSpace"); const auto regionChanges = model.addJob("regionTracker"); model.addJob("regionState", regionChanges); @@ -38,5 +39,15 @@ namespace workload { Engine::Engine(const WorkloadContextPointer& context) : Task("Engine", EngineBuilder::JobModel::create()), _context(context) { } + + + void PerformSpaceTransaction::configure(const Config& config) { + + } + void PerformSpaceTransaction::run(const WorkloadContextPointer& context) { + context->_space->enqueueFrame(); + context->_space->processTransactionQueue(); + } + } // namespace workload diff --git a/libraries/workload/src/workload/Engine.h b/libraries/workload/src/workload/Engine.h index aa45b03a20..773cfcb752 100644 --- a/libraries/workload/src/workload/Engine.h +++ b/libraries/workload/src/workload/Engine.h @@ -50,6 +50,24 @@ namespace workload { }; using EnginePointer = std::shared_ptr; + class PerformSpaceTransactionConfig : public Job::Config { + Q_OBJECT + public: + signals : + void dirty(); + + protected: + }; + + class PerformSpaceTransaction { + public: + using Config = PerformSpaceTransactionConfig; + using JobModel = Job::Model; + + void configure(const Config& config); + void run(const WorkloadContextPointer& context); + protected: + }; } // namespace workload #endif // hifi_workload_Space_h diff --git a/libraries/workload/src/workload/Proxy.h b/libraries/workload/src/workload/Proxy.h index b7f8094b00..9248c2bb31 100644 --- a/libraries/workload/src/workload/Proxy.h +++ b/libraries/workload/src/workload/Proxy.h @@ -21,10 +21,12 @@ public: Proxy(const Sphere& s) : sphere(s) {} Sphere sphere; - uint8_t region{ Region::UNKNOWN }; - uint8_t prevRegion{ Region::UNKNOWN }; + uint8_t region{ Region::INVALID }; + uint8_t prevRegion{ Region::INVALID }; uint16_t _padding; uint32_t _paddings[3]; + + using Vector = std::vector; }; diff --git a/libraries/workload/src/workload/Space.cpp b/libraries/workload/src/workload/Space.cpp index c2323fc0a1..45192b2a10 100644 --- a/libraries/workload/src/workload/Space.cpp +++ b/libraries/workload/src/workload/Space.cpp @@ -20,11 +20,80 @@ using namespace workload; -void Space::clear() { - _proxies.clear(); - _freeIndices.clear(); +Space::Space() : Collection() { + } + +void Space::processTransactionFrame(const Transaction& transaction) { + std::unique_lock lock(_proxiesMutex); + // Here we should be able to check the value of last ProxyID allocated + // and allocate new proxies accordingly + ProxyID maxID = _IDAllocator.getNumAllocatedIndices(); + if (maxID > _proxies.size()) { + _proxies.resize(maxID + 100); // allocate the maxId and more + } + // Now we know for sure that we have enough items in the array to + // capture anything coming from the transaction + + // resets and potential NEW items + processResets(transaction._resetItems); + + // Update the numItemsAtomic counter AFTER the reset changes went through + // _numAllocatedItems.exchange(maxID); + + // updates + processUpdates(transaction._updatedItems); + + // removes + processRemoves(transaction._removedItems); + + + // Update the numItemsAtomic counter AFTER the pending changes went through + // _numAllocatedItems.exchange(maxID); +} + +void Space::processResets(const Transaction::Resets& transactions) { + for (auto& reset : transactions) { + // Access the true item + auto ProxyID = std::get<0>(reset); + auto& item = _proxies[ProxyID]; + + // Reset the item with a new payload + item.sphere = (std::get<1>(reset)); + item.prevRegion = item.region = Region::UNKNOWN; + } +} + +void Space::processRemoves(const Transaction::Removes& transactions) { + for (auto removedID : transactions) { + _IDAllocator.freeIndex(removedID); + + // Access the true item + auto& item = _proxies[removedID]; + + // Kill it + item.prevRegion = item.region = Region::INVALID; + } +} + +void Space::processUpdates(const Transaction::Updates& transactions) { + for (auto& update : transactions) { + auto updateID = std::get<0>(update); + if (updateID == INVALID_PROXY_ID) { + continue; + } + + // Access the true item + auto& item = _proxies[updateID]; + + // Update the item + // item.update(std::get<1>(update)); + item.sphere = (std::get<1>(update)); + item.prevRegion = item.region = Region::UNKNOWN; + } +} +/* int32_t Space::createProxy(const Space::Sphere& newSphere) { if (_freeIndices.empty()) { _proxies.emplace_back(Space::Proxy(newSphere)); @@ -37,32 +106,25 @@ int32_t Space::createProxy(const Space::Sphere& newSphere) { _proxies[index].prevRegion = Region::UNKNOWN; return index; } -} - +}*/ +/* void Space::deleteProxies(const std::vector& deadIndices) { for (uint32_t i = 0; i < deadIndices.size(); ++i) { deleteProxy(deadIndices[i]); } } - -void Space::updateProxies(const std::vector& changedProxies) { +*/ +/*void Space::updateProxies(const std::vector& changedProxies) { for (uint32_t i = 0; i < changedProxies.size(); ++i) { int32_t proxyId = changedProxies[i].first; if (proxyId > -1 && proxyId < (int32_t)_proxies.size()) { updateProxy(changedProxies[i].first, changedProxies[i].second); } } -} - -void Space::setViews(const Views& views) { - _views = views; -} - -void Space::copyViews(std::vector& copy) const { - copy = _views; -} +}*/ void Space::categorizeAndGetChanges(std::vector& changes) { + std::unique_lock lock(_proxiesMutex); uint32_t numProxies = (uint32_t)_proxies.size(); uint32_t numViews = (uint32_t)_views.size(); for (uint32_t i = 0; i < numProxies; ++i) { @@ -92,7 +154,7 @@ void Space::categorizeAndGetChanges(std::vector& changes) { } // private -void Space::deleteProxy(int32_t proxyId) { +/*void Space::deleteProxy(int32_t proxyId) { if (proxyId > -1 && proxyId < (int32_t)_proxies.size()) { if (proxyId == (int32_t)_proxies.size() - 1) { // remove proxy on back @@ -110,9 +172,10 @@ void Space::deleteProxy(int32_t proxyId) { _freeIndices.push_back(proxyId); } } -} +}*/ uint32_t Space::copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const { + std::unique_lock lock(_proxiesMutex); auto numCopied = std::min(numDestProxies, (uint32_t)_proxies.size()); memcpy(proxies, _proxies.data(), numCopied * sizeof(Proxy)); return numCopied; @@ -120,9 +183,24 @@ uint32_t Space::copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const { // private -void Space::updateProxy(int32_t proxyId, const Space::Sphere& newSphere) { +/*void Space::updateProxy(int32_t proxyId, const Space::Sphere& newSphere) { if (proxyId > -1 && proxyId < (int32_t)_proxies.size()) { _proxies[proxyId].sphere = newSphere; } +}*/ + +void Space::clear() { + std::unique_lock lock(_proxiesMutex); + _IDAllocator.clear(); + _proxies.clear(); + _views.clear(); } + +void Space::setViews(const Views& views) { + _views = views; +} + +void Space::copyViews(std::vector& copy) const { + copy = _views; +} diff --git a/libraries/workload/src/workload/Space.h b/libraries/workload/src/workload/Space.h index 699d44f7a1..200d8d9ac3 100644 --- a/libraries/workload/src/workload/Space.h +++ b/libraries/workload/src/workload/Space.h @@ -23,7 +23,7 @@ namespace workload { -class Space { +class Space : public Collection { public: using ProxyUpdate = std::pair; @@ -35,55 +35,40 @@ public: uint8_t prevRegion { 0 }; }; - Space() {} + Space(); - // This call is thread safe, can be called from anywhere to allocate a new ID - ProxyID allocateID(); - - // Check that the ID is valid and allocated for this space, this a threadsafe call - bool isAllocatedID(const ProxyID& id) const; - - // THis is the total number of allocated proxies, this a threadsafe call - Index getNumAllocatedProxies() const { return _numAllocatedProxies.load(); } - - // Enqueue transaction to the space - void enqueueTransaction(const Transaction& transaction); - - // Enqueue transaction to the space - void enqueueTransaction(Transaction&& transaction); - - // Enqueue end of frame transactions boundary - uint32_t enqueueFrame(); - - // Process the pending transactions queued - void processTransactionQueue(); - - void setViews(const std::vector& views); + void setViews(const Views& views); uint32_t getNumViews() const { return (uint32_t)(_views.size()); } void copyViews(std::vector& copy) const; - - uint32_t getNumObjects() const { return (uint32_t)(_proxies.size() - _freeIndices.size()); } - uint32_t getNumAllocatedProxies() const { return (uint32_t)(_proxies.size()); } + uint32_t getNumObjects() const { return _IDAllocator.getNumLiveIndices(); } // (uint32_t)(_proxies.size() - _freeIndices.size()); } + uint32_t getNumAllocatedProxies() const { return (uint32_t)(_IDAllocator.getNumAllocatedIndices()); } void categorizeAndGetChanges(std::vector& changes); uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const; + void clear(); private: + virtual void processTransactionFrame(const Transaction& transaction); + void processResets(const Transaction::Resets& transactions); + void processRemoves(const Transaction::Removes& transactions); + void processUpdates(const Transaction::Updates& transactions); - void clear(); - int32_t createProxy(const Sphere& sphere); - void deleteProxies(const IndexVector& deadIndices); - void updateProxies(const std::vector& changedProxies); + // int32_t createProxy(const Sphere& sphere); + // void deleteProxies(const IndexVector& deadIndices); + // void updateProxies(const std::vector& changedProxies); - void deleteProxy(int32_t proxyId); - void updateProxy(int32_t proxyId, const Sphere& sphere); + // void deleteProxy(int32_t proxyId); + // void updateProxy(int32_t proxyId, const Sphere& sphere); + + + // The database of proxies is protected for editing by a mutex + mutable std::mutex _proxiesMutex; + Proxy::Vector _proxies; - std::vector _proxies; Views _views; - IndexVector _freeIndices; }; using SpacePointer = std::shared_ptr; diff --git a/libraries/workload/src/workload/Transaction.cpp b/libraries/workload/src/workload/Transaction.cpp index 3a5e0b31d8..063551763a 100644 --- a/libraries/workload/src/workload/Transaction.cpp +++ b/libraries/workload/src/workload/Transaction.cpp @@ -13,21 +13,16 @@ using namespace workload; -void Transaction::resetItem(ItemID id, PayloadPointer& payload) { - if (payload) { - _resetItems.emplace_back(Reset{ id, payload }); - } else { - qCDebug(renderlogging) << "WARNING: Transaction::resetItem with a null payload!"; - removeItem(id); - } +void Transaction::reset(ProxyID id, const ProxyPayload& payload) { + _resetItems.emplace_back(Reset{ id, payload }); } -void Transaction::removeItem(ItemID id) { +void Transaction::remove(ProxyID id) { _removedItems.emplace_back(id); } -void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) { - _updatedItems.emplace_back(id, functor); +void Transaction::update(ProxyID id, const ProxyPayload& payload) { + _updatedItems.emplace_back(id, payload); } void Transaction::reserve(const std::vector& transactionContainer) { @@ -77,6 +72,18 @@ void copyElements(T& target, const T& source) { } +void Transaction::reset(const Resets& resets) { + copyElements(_resetItems, resets); +} + +void Transaction::remove(const Removes& removes) { + copyElements(_removedItems, removes); +} + +void Transaction::update(const Updates& updates) { + copyElements(_updatedItems, updates); +} + void Transaction::merge(Transaction&& transaction) { moveElements(_resetItems, transaction._resetItems); moveElements(_removedItems, transaction._removedItems); @@ -99,23 +106,22 @@ void Transaction::clear() { Collection::Collection() { - _items.push_back(Item()); // add the itemID #0 to nothing + //_items.push_back(Item()); // add the ProxyID #0 to nothing } Collection::~Collection() { - qCDebug(renderlogging) << "Scene::~Scene()"; } -ItemID Collection::allocateID() { - // Just increment and return the proevious value initialized at 0 - return _IDAllocator.fetch_add(1); +ProxyID Collection::allocateID() { + // Just increment and return the previous value initialized at 0 + return _IDAllocator.allocateIndex(); } -bool Collection::isAllocatedID(const ItemID& id) const { - return Item::isValidID(id) && (id < _numAllocatedItems.load()); +bool Collection::isAllocatedID(const ProxyID& id) const { + return _IDAllocator.checkIndex(id); } -/// Enqueue change batch to the scene +/// Enqueue change batch to the Collection void Collection::enqueueTransaction(const Transaction& transaction) { std::unique_lock lock(_transactionQueueMutex); _transactionQueue.emplace_back(transaction); @@ -127,7 +133,6 @@ void Collection::enqueueTransaction(Transaction&& transaction) { } uint32_t Collection::enqueueFrame() { - PROFILE_RANGE(render, __FUNCTION__); TransactionQueue localTransactionQueue; { std::unique_lock lock(_transactionQueueMutex); @@ -146,8 +151,6 @@ uint32_t Collection::enqueueFrame() { void Collection::processTransactionQueue() { - PROFILE_RANGE(render, __FUNCTION__); - static TransactionFrames queuedFrames; { // capture the queued frames and clear the queue @@ -163,13 +166,12 @@ void Collection::processTransactionQueue() { queuedFrames.clear(); } -void Collection::processTransactionFrame(const Transaction& transaction) { - PROFILE_RANGE(render, __FUNCTION__); - { +//void Collection::processTransactionFrame(const Transaction& transaction) { + /** std::unique_lock lock(_itemsMutex); - // Here we should be able to check the value of last ItemID allocated + // Here we should be able to check the value of last ProxyID allocated // and allocate new items accordingly - ItemID maxID = _IDAllocator.load(); + ProxyID maxID = _IDAllocator.getNumAllocatedIndices(); if (maxID > _items.size()) { _items.resize(maxID + 100); // allocate the maxId and more } @@ -195,32 +197,32 @@ void Collection::processTransactionFrame(const Transaction& transaction) { // Update the numItemsAtomic counter AFTER the pending changes went through _numAllocatedItems.exchange(maxID); - } -} + }*/ +//} -void Collection::resetItems(const Transaction::Resets& transactions) { - for (auto& reset : transactions) { +//void Collection::resetItems(const Transaction::Resets& transactions) { + /* for (auto& reset : transactions) { // Access the true item - auto itemId = std::get<0>(reset); - auto& item = _items[itemId]; + auto ProxyID = std::get<0>(reset); + auto& item = _items[ProxyID]; // Reset the item with a new payload item.resetPayload(std::get<1>(reset)); - } -} + }*/ +//} -void Collection::removeItems(const Transaction::Removes& transactions) { - for (auto removedID : transactions) { +//void Collection::removeItems(const Transaction::Removes& transactions) { + /* for (auto removedID : transactions) { // Access the true item auto& item = _items[removedID]; // Kill it item.kill(); - } -} + }*/ +//} -void Collection::updateItems(const Transaction::Updates& transactions) { - for (auto& update : transactions) { +//void Collection::updateItems(const Transaction::Updates& transactions) { + /* for (auto& update : transactions) { auto updateID = std::get<0>(update); if (updateID == Item::INVALID_ITEM_ID) { continue; @@ -231,5 +233,5 @@ void Collection::updateItems(const Transaction::Updates& transactions) { // Update the item item.update(std::get<1>(update)); - } -} \ No newline at end of file + }*/ +//} \ No newline at end of file diff --git a/libraries/workload/src/workload/Transaction.h b/libraries/workload/src/workload/Transaction.h index 098e953f36..d078989f48 100644 --- a/libraries/workload/src/workload/Transaction.h +++ b/libraries/workload/src/workload/Transaction.h @@ -38,7 +38,7 @@ namespace workload { Index _nextNewIndex{ 0 }; bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex)); } - Index getNumIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); } + Index getNumLiveIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); } Index getNumFreeIndices() const { return (Index)_freeIndices.size(); } Index getNumAllocatedIndices() const { return _nextNewIndex; } @@ -64,6 +64,7 @@ namespace workload { void freeIndex(Index index) { if (checkIndex(index)) { _freeIndices.push_back(index); + //std::sort(_freeIndices.begin(), _freeIndices.end()); } } @@ -79,7 +80,7 @@ namespace workload { using IndexVector = indexed_container::Indices; using ProxyID = Index; - + const ProxyID INVALID_PROXY_ID{ indexed_container ::INVALID_INDEX }; // Transaction is the mechanism to make any change to the Space. // Whenever a new proxy need to be reset, @@ -94,16 +95,27 @@ class Transaction { public: using ProxyPayload = Sphere; + using Reset = std::tuple; + using Remove = ProxyID; + using Update = std::tuple; + + using Resets = std::vector; + using Removes = std::vector; + using Updates = std::vector; + Transaction() {} ~Transaction() {} // Proxy transactions void reset(ProxyID id, const ProxyPayload& sphere); + void reset(const Resets& resets); void remove(ProxyID id); + void remove(const Removes& removes); bool hasRemovals() const { return !_removedItems.empty(); } void update(ProxyID id, const ProxyPayload& sphere); - + void update(const Updates& updates); + void reserve(const std::vector& transactionContainer); void merge(const std::vector& transactionContainer); void merge(std::vector&& transactionContainer); @@ -113,13 +125,6 @@ public: protected: - using Reset = std::tuple; - using Remove = ProxyID; - using Update = std::tuple; - - using Resets = std::vector; - using Removes = std::vector; - using Updates = std::vector; Resets _resetItems; Removes _removedItems; @@ -129,6 +134,8 @@ typedef std::vector TransactionQueue; class Collection { public: + Collection(); + ~Collection(); // This call is thread safe, can be called from anywhere to allocate a new ID ProxyID allocateID(); @@ -149,7 +156,7 @@ public: uint32_t enqueueFrame(); // Process the pending transactions queued - void processTransactionQueue(); + virtual void processTransactionQueue(); protected: @@ -168,7 +175,7 @@ protected: uint32_t _transactionFrameNumber{ 0 }; // Process one transaction frame - void processTransactionFrame(const Transaction& transaction); + virtual void processTransactionFrame(const Transaction& transaction) = 0; }; } // namespace workload From 727ac91fb331f2dbae58b8a04bfa48ea72e38ff1 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Mar 2018 16:11:41 -0700 Subject: [PATCH 4/4] fix warnings and comments --- libraries/workload/src/workload/Space.cpp | 2 +- libraries/workload/src/workload/Space.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/workload/src/workload/Space.cpp b/libraries/workload/src/workload/Space.cpp index 45192b2a10..0e7b9a5ebc 100644 --- a/libraries/workload/src/workload/Space.cpp +++ b/libraries/workload/src/workload/Space.cpp @@ -30,7 +30,7 @@ void Space::processTransactionFrame(const Transaction& transaction) { // Here we should be able to check the value of last ProxyID allocated // and allocate new proxies accordingly ProxyID maxID = _IDAllocator.getNumAllocatedIndices(); - if (maxID > _proxies.size()) { + if (maxID > (Index) _proxies.size()) { _proxies.resize(maxID + 100); // allocate the maxId and more } // Now we know for sure that we have enough items in the array to diff --git a/libraries/workload/src/workload/Space.h b/libraries/workload/src/workload/Space.h index 200d8d9ac3..bed013cce9 100644 --- a/libraries/workload/src/workload/Space.h +++ b/libraries/workload/src/workload/Space.h @@ -51,7 +51,7 @@ public: void clear(); private: - virtual void processTransactionFrame(const Transaction& transaction); + void processTransactionFrame(const Transaction& transaction) override; void processResets(const Transaction::Resets& transactions); void processRemoves(const Transaction::Removes& transactions); void processUpdates(const Transaction::Updates& transactions);