mirror of
https://github.com/lubosz/overte.git
synced 2025-04-13 13:13:19 +02:00
FIrst step introducing the Transaction model to access the Proxy of the space
This commit is contained in:
parent
74be67edb1
commit
ea9dee1286
4 changed files with 468 additions and 20 deletions
38
libraries/workload/src/workload/Proxy.h
Normal file
38
libraries/workload/src/workload/Proxy.h
Normal 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
|
|
@ -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);
|
||||
|
||||
|
|
235
libraries/workload/src/workload/Transaction.cpp
Normal file
235
libraries/workload/src/workload/Transaction.cpp
Normal 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));
|
||||
}
|
||||
}
|
166
libraries/workload/src/workload/Transaction.h
Normal file
166
libraries/workload/src/workload/Transaction.h
Normal 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
|
Loading…
Reference in a new issue