Merge pull request #12636 from samcake/workload

Workload: Applying the transaction model to the Space & Proxies
This commit is contained in:
Andrew Meadows 2018-03-14 16:53:24 -07:00 committed by GitHub
commit 3a3bc62bd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 621 additions and 60 deletions

View file

@ -56,7 +56,7 @@ void GameSpaceToRender::run(const workload::WorkloadContextPointer& runContext,
return;
}
std::vector<workload::Space::Proxy> 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<workload::Space::Proxy>& 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<gpu::Buffer>(sizeOfProxy);
}

View file

@ -63,7 +63,7 @@ public:
void showProxies(bool show);
void showViews(bool show);
void setAllProxies(const std::vector<workload::Space::Proxy>& 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<workload::Space::Proxy> _myOwnProxies;
workload::Proxy::Vector _myOwnProxies;
gpu::BufferPointer _allProxiesBuffer;
uint32_t _numAllProxies{ 0 };

View file

@ -281,8 +281,11 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
}
if (entity->getSpaceIndex() == -1) {
std::unique_lock<std::mutex> 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<std::mutex> lock(_spaceLock);
_space->updateProxies(_spaceUpdates);
spaceTransaction.update(_spaceUpdates);
_spaceUpdates.clear();
}
{
std::vector<int32_t> staleProxies;
tree->swapStaleProxies(staleProxies);
spaceTransaction.remove(staleProxies);
{
std::unique_lock<std::mutex> lock(_spaceLock);
_space->deleteProxies(staleProxies);
_space->enqueueTransaction(spaceTransaction);
}
}
@ -458,7 +463,7 @@ void EntityTreeRenderer::update(bool simulate) {
void EntityTreeRenderer::handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUpdate) {
std::unique_lock<std::mutex> lock(_spaceLock);
_spaceUpdates.push_back(proxyUpdate);
_spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second);
}
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {

View file

@ -270,7 +270,7 @@ private:
mutable std::mutex _spaceLock;
workload::SpacePointer _space{ new workload::Space() };
std::vector<workload::Space::ProxyUpdate> _spaceUpdates;
workload::Transaction::Updates _spaceUpdates;
};

View file

@ -29,6 +29,7 @@ namespace workload {
using JobModel = Task::ModelI<EngineBuilder, Inputs>;
void build(JobModel& model, const Varying& in, Varying& out) {
model.addJob<SetupViews>("setupViews", in);
model.addJob<PerformSpaceTransaction>("updateSpace");
const auto regionChanges = model.addJob<RegionTracker>("regionTracker");
model.addJob<RegionState>("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

View file

@ -50,6 +50,24 @@ namespace workload {
};
using EnginePointer = std::shared_ptr<Engine>;
class PerformSpaceTransactionConfig : public Job::Config {
Q_OBJECT
public:
signals :
void dirty();
protected:
};
class PerformSpaceTransaction {
public:
using Config = PerformSpaceTransactionConfig;
using JobModel = Job::Model<PerformSpaceTransaction, Config>;
void configure(const Config& config);
void run(const WorkloadContextPointer& context);
protected:
};
} // namespace workload
#endif // hifi_workload_Space_h

View file

@ -0,0 +1,35 @@
//
// 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 {
class Proxy {
public:
Proxy() : sphere(0.0f) {}
Proxy(const Sphere& s) : sphere(s) {}
Sphere sphere;
uint8_t region{ Region::INVALID };
uint8_t prevRegion{ Region::INVALID };
uint16_t _padding;
uint32_t _paddings[3];
using Vector = std::vector<Proxy>;
};
} // namespace workload
#endif // hifi_workload_Proxy_h

View file

@ -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<std::mutex> 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 > (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
// 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<int32_t>& deadIndices) {
for (uint32_t i = 0; i < deadIndices.size(); ++i) {
deleteProxy(deadIndices[i]);
}
}
void Space::updateProxies(const std::vector<ProxyUpdate>& changedProxies) {
*/
/*void Space::updateProxies(const std::vector<ProxyUpdate>& 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<View>& copy) const {
copy = _views;
}
}*/
void Space::categorizeAndGetChanges(std::vector<Space::Change>& changes) {
std::unique_lock<std::mutex> 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<Space::Change>& 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<std::mutex> 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<std::mutex> lock(_proxiesMutex);
_IDAllocator.clear();
_proxies.clear();
_views.clear();
}
void Space::setViews(const Views& views) {
_views = views;
}
void Space::copyViews(std::vector<View>& copy) const {
copy = _views;
}

View file

@ -19,29 +19,14 @@
#include <vector>
#include <glm/glm.hpp>
#include "View.h"
#include "Transaction.h"
namespace workload {
using IndexVector = std::vector<int32_t>;
class Space {
class Space : public Collection {
public:
using Sphere = glm::vec4; // <x,y,z> = center, w = radius
using ProxyUpdate = std::pair<int32_t, Sphere>;
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) {}
@ -50,31 +35,40 @@ public:
uint8_t prevRegion { 0 };
};
Space() {}
Space();
void clear();
int32_t createProxy(const Sphere& sphere);
void deleteProxies(const IndexVector& deadIndices);
void updateProxies(const std::vector<ProxyUpdate>& changedProxies);
void setViews(const std::vector<View>& views);
void setViews(const Views& views);
uint32_t getNumViews() const { return (uint32_t)(_views.size()); }
void copyViews(std::vector<View>& 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<Change>& changes);
uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const;
void clear();
private:
void deleteProxy(int32_t proxyId);
void updateProxy(int32_t proxyId, const Sphere& sphere);
std::vector<Proxy> _proxies;
void processTransactionFrame(const Transaction& transaction) override;
void processResets(const Transaction::Resets& transactions);
void processRemoves(const Transaction::Removes& transactions);
void processUpdates(const Transaction::Updates& transactions);
// int32_t createProxy(const Sphere& sphere);
// void deleteProxies(const IndexVector& deadIndices);
// void updateProxies(const std::vector<ProxyUpdate>& changedProxies);
// 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;
Views _views;
IndexVector _freeIndices;
};
using SpacePointer = std::shared_ptr<Space>;

View file

@ -0,0 +1,237 @@
//
// 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::reset(ProxyID id, const ProxyPayload& payload) {
_resetItems.emplace_back(Reset{ id, payload });
}
void Transaction::remove(ProxyID id) {
_removedItems.emplace_back(id);
}
void Transaction::update(ProxyID id, const ProxyPayload& payload) {
_updatedItems.emplace_back(id, payload);
}
void Transaction::reserve(const std::vector<Transaction>& 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<Transaction>& transactionContainer) {
reserve(transactionContainer);
for (const auto& transaction : transactionContainer) {
merge(transaction);
}
}
void Transaction::merge(std::vector<Transaction>&& 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 <typename T>
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 <typename T>
void copyElements(T& target, const T& source) {
target.insert(target.end(), source.begin(), source.end());
}
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);
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 ProxyID #0 to nothing
}
Collection::~Collection() {
}
ProxyID Collection::allocateID() {
// Just increment and return the previous value initialized at 0
return _IDAllocator.allocateIndex();
}
bool Collection::isAllocatedID(const ProxyID& id) const {
return _IDAllocator.checkIndex(id);
}
/// Enqueue change batch to the Collection
void Collection::enqueueTransaction(const Transaction& transaction) {
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
_transactionQueue.emplace_back(transaction);
}
void Collection::enqueueTransaction(Transaction&& transaction) {
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
_transactionQueue.emplace_back(std::move(transaction));
}
uint32_t Collection::enqueueFrame() {
TransactionQueue localTransactionQueue;
{
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
localTransactionQueue.swap(_transactionQueue);
}
Transaction consolidatedTransaction;
consolidatedTransaction.merge(std::move(localTransactionQueue));
{
std::unique_lock<std::mutex> lock(_transactionFramesMutex);
_transactionFrames.push_back(consolidatedTransaction);
}
return ++_transactionFrameNumber;
}
void Collection::processTransactionQueue() {
static TransactionFrames queuedFrames;
{
// capture the queued frames and clear the queue
std::unique_lock<std::mutex> 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) {
/**
std::unique_lock<std::mutex> lock(_itemsMutex);
// Here we should be able to check the value of last ProxyID allocated
// and allocate new items accordingly
ProxyID maxID = _IDAllocator.getNumAllocatedIndices();
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 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) {
// 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));
}*/
//}

View file

@ -0,0 +1,183 @@
//
// 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 <atomic>
#include <mutex>
#include <memory>
#include <vector>
#include <glm/glm.hpp>
#include "Proxy.h"
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 <Index MaxNumElements = MAXIMUM_INDEX>
class Allocator {
public:
Allocator() {}
Indices _freeIndices;
Index _nextNewIndex{ 0 };
bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex)); }
Index getNumLiveIndices() 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);
//std::sort(_freeIndices.begin(), _freeIndices.end());
}
}
void clear() {
_freeIndices.clear();
_nextNewIndex = 0;
}
};
}
using Index = indexed_container::Index;
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,
// 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;
using Reset = std::tuple<ProxyID, ProxyPayload>;
using Remove = ProxyID;
using Update = std::tuple<ProxyID, ProxyPayload>;
using Resets = std::vector<Reset>;
using Removes = std::vector<Remove>;
using Updates = std::vector<Update>;
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<Transaction>& transactionContainer);
void merge(const std::vector<Transaction>& transactionContainer);
void merge(std::vector<Transaction>&& transactionContainer);
void merge(const Transaction& transaction);
void merge(Transaction&& transaction);
void clear();
protected:
Resets _resetItems;
Removes _removedItems;
Updates _updatedItems;
};
typedef std::vector<Transaction> TransactionQueue;
class Collection {
public:
Collection();
~Collection();
// 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 _IDAllocator.getNumAllocatedIndices(); }
// 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
virtual void processTransactionQueue();
protected:
// Thread safe elements that can be accessed from anywhere
indexed_container::Allocator<> _IDAllocator;
//std::atomic<unsigned int> _IDAllocator{ 1 }; // first valid itemID will be One
//std::atomic<unsigned int> _numAllocatedItems{ 1 }; // num of allocated items, matching the _items.size()
std::mutex _transactionQueueMutex;
TransactionQueue _transactionQueue;
std::mutex _transactionFramesMutex;
using TransactionFrames = std::vector<Transaction>;
TransactionFrames _transactionFrames;
uint32_t _transactionFrameNumber{ 0 };
// Process one transaction frame
virtual void processTransactionFrame(const Transaction& transaction) = 0;
};
} // namespace workload
#endif // hifi_workload_Transaction_h