FIrst step introducing the Transaction model to access the Proxy of the space

This commit is contained in:
samcake 2018-03-12 17:49:45 -07:00
parent 74be67edb1
commit ea9dee1286
4 changed files with 468 additions and 20 deletions

View file

@ -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<Index>;
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

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 {
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) {}
@ -52,10 +37,27 @@ public:
Space() {}
void clear();
int32_t createProxy(const Sphere& sphere);
void deleteProxies(const IndexVector& deadIndices);
void updateProxies(const std::vector<ProxyUpdate>& 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<View>& 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<ProxyUpdate>& changedProxies);
void deleteProxy(int32_t proxyId);
void updateProxy(int32_t proxyId, const Sphere& sphere);

View file

@ -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<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::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<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() {
PROFILE_RANGE(render, __FUNCTION__);
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() {
PROFILE_RANGE(render, __FUNCTION__);
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) {
PROFILE_RANGE(render, __FUNCTION__);
{
std::unique_lock<std::mutex> 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));
}
}

View file

@ -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 <memory>
#include <vector>
#include <glm/glm.hpp>
#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<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:
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>;
Resets _resetItems;
Removes _removedItems;
Updates _updatedItems;
};
typedef std::vector<Transaction> 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 <Index MaxNumElements = MAXIMUM_INDEX>
class Allocator {
public:
Allocator() {}
Indices _freeIndices;
std::atomic<int32_t> _nextNewIndex{ 0 };
std::atomic<int32_t> _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<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
void processTransactionFrame(const Transaction& transaction);
};
} // namespace workload
#endif // hifi_workload_Transaction_h