mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-12 01:22:41 +02:00
Working on tests for metavoxel streaming.
This commit is contained in:
parent
07e54b5162
commit
7a9c0f0e6a
4 changed files with 229 additions and 33 deletions
|
@ -604,10 +604,79 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO
|
|||
|
||||
void MetavoxelData::readIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD,
|
||||
Bitstream& in, const MetavoxelLOD& lod) {
|
||||
|
||||
// shallow copy the reference
|
||||
*this = reference;
|
||||
|
||||
// if nothing changed, return
|
||||
bool changed;
|
||||
in >> changed;
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// expand if the size has changed
|
||||
bool sizeChanged;
|
||||
in >> sizeChanged;
|
||||
if (sizeChanged) {
|
||||
float size;
|
||||
in >> size;
|
||||
while (_size < size) {
|
||||
expand();
|
||||
}
|
||||
}
|
||||
|
||||
// read the nodes removed
|
||||
forever {
|
||||
AttributePointer attribute;
|
||||
in >> attribute;
|
||||
if (!attribute) {
|
||||
break;
|
||||
}
|
||||
_roots.take(attribute)->decrementReferenceCount(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelData::writeIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD,
|
||||
Bitstream& out, const MetavoxelLOD& lod) const {
|
||||
|
||||
// first things first: there might be no change whatsoever
|
||||
glm::vec3 minimum = getMinimum();
|
||||
bool becameSubdivided = lod.becameSubdivided(minimum, _size, referenceLOD);
|
||||
if (_size == reference._size && _roots == reference._roots && !becameSubdivided) {
|
||||
out << false;
|
||||
return;
|
||||
}
|
||||
out << true;
|
||||
|
||||
// compare the size; if changed (rare), we must compare to the expanded reference
|
||||
const MetavoxelData* expandedReference = &reference;
|
||||
if (_size == reference._size) {
|
||||
out << false;
|
||||
} else {
|
||||
out << true;
|
||||
out << _size;
|
||||
|
||||
MetavoxelData* expanded = new MetavoxelData(reference);
|
||||
while (expanded->_size < _size) {
|
||||
expanded->expand();
|
||||
}
|
||||
expandedReference = expanded;
|
||||
}
|
||||
|
||||
// write the nodes removed
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = expandedReference->_roots.constBegin();
|
||||
it != expandedReference->_roots.constEnd(); it++) {
|
||||
if (!_roots.contains(it.key())) {
|
||||
out << it.key();
|
||||
}
|
||||
}
|
||||
out << AttributePointer();
|
||||
|
||||
// delete the expanded reference if we had to expand
|
||||
if (expandedReference != &reference) {
|
||||
delete expandedReference;
|
||||
}
|
||||
}
|
||||
|
||||
MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) {
|
||||
|
|
|
@ -34,7 +34,8 @@ class NetworkValue;
|
|||
class Spanner;
|
||||
class SpannerRenderer;
|
||||
|
||||
/// Determines whether to subdivide each node when traversing.
|
||||
/// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a
|
||||
/// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided.
|
||||
class MetavoxelLOD {
|
||||
STREAMABLE
|
||||
|
||||
|
@ -46,6 +47,7 @@ public:
|
|||
|
||||
bool isValid() const { return threshold > 0.0f; }
|
||||
|
||||
/// Checks whether, according to this LOD, we should subdivide the described voxel.
|
||||
bool shouldSubdivide(const glm::vec3& minimum, float size, float multiplier = 1.0f) const;
|
||||
|
||||
/// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference.
|
||||
|
@ -78,20 +80,25 @@ public:
|
|||
/// Applies the specified visitor to the contained voxels.
|
||||
void guide(MetavoxelVisitor& visitor);
|
||||
|
||||
/// Inserts a spanner into the specified attribute layer.
|
||||
void insert(const AttributePointer& attribute, const SharedObjectPointer& object);
|
||||
void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
|
||||
|
||||
/// Removes a spanner from the specified attribute layer.
|
||||
void remove(const AttributePointer& attribute, const SharedObjectPointer& object);
|
||||
void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
|
||||
|
||||
/// Toggles the existence of a spanner in the specified attribute layer (removes if present, adds if not).
|
||||
void toggle(const AttributePointer& attribute, const SharedObjectPointer& object);
|
||||
void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
|
||||
|
||||
/// Replaces a spanner in the specified attribute layer.
|
||||
void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject,
|
||||
const SharedObjectPointer& newObject);
|
||||
void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject,
|
||||
const SharedObjectPointer& newObject);
|
||||
|
||||
|
||||
/// Clears all data in the specified attribute layer.
|
||||
void clear(const AttributePointer& attribute);
|
||||
|
||||
/// Convenience function that finds the first spanner intersecting the provided ray.
|
||||
|
@ -101,7 +108,7 @@ public:
|
|||
/// Sets part of the data.
|
||||
void set(const glm::vec3& minimum, const MetavoxelData& data, bool blend = false);
|
||||
|
||||
/// Expands the tree, increasing its capacity in all dimensions.
|
||||
/// Expands the tree, doubling its size in all dimensions (that is, increasing its volume eightfold).
|
||||
void expand();
|
||||
|
||||
void read(Bitstream& in, const MetavoxelLOD& lod = MetavoxelLOD());
|
||||
|
|
|
@ -321,44 +321,76 @@ static bool testSerialization(Bitstream::MetadataType metadataType) {
|
|||
}
|
||||
|
||||
bool MetavoxelTests::run() {
|
||||
|
||||
qDebug() << "Running transmission tests...";
|
||||
qDebug();
|
||||
|
||||
// seed the random number generator so that our tests are reproducible
|
||||
srand(0xBAAAAABE);
|
||||
|
||||
// create two endpoints with the same header
|
||||
// check for an optional command line argument specifying a single test
|
||||
QStringList arguments = this->arguments();
|
||||
int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0;
|
||||
|
||||
QByteArray datagramHeader("testheader");
|
||||
Endpoint alice(datagramHeader), bob(datagramHeader);
|
||||
|
||||
alice.setOther(&bob);
|
||||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
const int SIMULATION_ITERATIONS = 10000;
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
if (test == 0 || test == 1) {
|
||||
qDebug() << "Running transmission tests...";
|
||||
qDebug();
|
||||
|
||||
// create two endpoints with the same header
|
||||
Endpoint alice(datagramHeader), bob(datagramHeader);
|
||||
|
||||
alice.setOther(&bob);
|
||||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived;
|
||||
qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived;
|
||||
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
|
||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
|
||||
qDebug();
|
||||
}
|
||||
|
||||
if (test == 0 || test == 2) {
|
||||
qDebug() << "Running serialization tests...";
|
||||
qDebug();
|
||||
|
||||
if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived;
|
||||
qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived;
|
||||
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
|
||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
|
||||
qDebug();
|
||||
if (test == 0 || test == 3) {
|
||||
qDebug() << "Running metavoxel data tests...";
|
||||
qDebug();
|
||||
|
||||
qDebug() << "Running serialization tests...";
|
||||
qDebug();
|
||||
// clear the stats
|
||||
datagramsSent = bytesSent = datagramsReceived = bytesReceived = 0;
|
||||
|
||||
if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) {
|
||||
return true;
|
||||
// create client and server endpoints
|
||||
Endpoint client(datagramHeader, Endpoint::METAVOXEL_CLIENT_MODE);
|
||||
Endpoint server(datagramHeader, Endpoint::METAVOXEL_SERVER_MODE);
|
||||
|
||||
client.setOther(&server);
|
||||
server.setOther(&client);
|
||||
|
||||
// simulate
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (client.simulate(i) || server.simulate(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
}
|
||||
|
||||
qDebug() << "All tests passed!";
|
||||
|
@ -375,7 +407,8 @@ static SharedObjectPointer createRandomSharedObject() {
|
|||
}
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
||||
Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) :
|
||||
_mode(mode),
|
||||
_sequencer(new DatagramSequencer(datagramHeader, this)),
|
||||
_highPriorityMessagesToSend(0.0f),
|
||||
_reliableMessagesToSend(0.0f) {
|
||||
|
@ -396,6 +429,13 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
|||
ReceiveRecord receiveRecord = { 0 };
|
||||
_receiveRecords.append(receiveRecord);
|
||||
|
||||
if (mode == METAVOXEL_CLIENT_MODE) {
|
||||
_lod = MetavoxelLOD(glm::vec3(), 0.01f);
|
||||
return;
|
||||
}
|
||||
if (mode == METAVOXEL_SERVER_MODE) {
|
||||
return;
|
||||
}
|
||||
// create the object that represents out delta-encoded state
|
||||
_localState = new TestSharedObjectA();
|
||||
|
||||
|
@ -415,7 +455,7 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
|||
QByteArray bytes = createRandomBytes(MIN_STREAM_BYTES, MAX_STREAM_BYTES);
|
||||
_dataStreamed.append(bytes);
|
||||
output->getBuffer().write(bytes);
|
||||
streamedBytesSent += bytes.size();
|
||||
streamedBytesSent += bytes.size();
|
||||
}
|
||||
|
||||
static QVariant createRandomMessage() {
|
||||
|
@ -525,6 +565,34 @@ bool Endpoint::simulate(int iterationNumber) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_mode == METAVOXEL_CLIENT_MODE) {
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
|
||||
ClientStateMessage state = { _lod };
|
||||
out << QVariant::fromValue(state);
|
||||
_sequencer->endPacket();
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), MetavoxelData(), _lod };
|
||||
_sendRecords.append(record);
|
||||
return false;
|
||||
}
|
||||
if (_mode == METAVOXEL_SERVER_MODE) {
|
||||
// wait until we have a valid lod
|
||||
if (!_lod.isValid()) {
|
||||
return false;
|
||||
}
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
out << QVariant::fromValue(MetavoxelDeltaMessage());
|
||||
_data.writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod);
|
||||
_sequencer->endPacket();
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), _data, _lod };
|
||||
_sendRecords.append(record);
|
||||
return false;
|
||||
}
|
||||
|
||||
// enqueue some number of high priority messages
|
||||
const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f;
|
||||
const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f;
|
||||
|
@ -619,6 +687,28 @@ void Endpoint::handleHighPriorityMessage(const QVariant& message) {
|
|||
}
|
||||
|
||||
void Endpoint::readMessage(Bitstream& in) {
|
||||
if (_mode == METAVOXEL_CLIENT_MODE) {
|
||||
QVariant message;
|
||||
in >> message;
|
||||
handleMessage(message, in);
|
||||
|
||||
// record the receipt
|
||||
ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), SharedObjectPointer(),
|
||||
_data, _sendRecords.first().lod };
|
||||
_receiveRecords.append(record);
|
||||
return;
|
||||
}
|
||||
if (_mode == METAVOXEL_SERVER_MODE) {
|
||||
QVariant message;
|
||||
in >> message;
|
||||
handleMessage(message, in);
|
||||
|
||||
// record the receipt
|
||||
ReceiveRecord record = { _sequencer->getIncomingPacketNumber() };
|
||||
_receiveRecords.append(record);
|
||||
return;
|
||||
}
|
||||
|
||||
SequencedTestMessage message;
|
||||
in >> message;
|
||||
|
||||
|
@ -682,6 +772,22 @@ void Endpoint::clearReceiveRecordsBefore(int index) {
|
|||
_receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1);
|
||||
}
|
||||
|
||||
void Endpoint::handleMessage(const QVariant& message, Bitstream& in) {
|
||||
int userType = message.userType();
|
||||
if (userType == ClientStateMessage::Type) {
|
||||
ClientStateMessage state = message.value<ClientStateMessage>();
|
||||
_lod = state.lod;
|
||||
|
||||
} else if (userType == MetavoxelDeltaMessage::Type) {
|
||||
_data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod);
|
||||
|
||||
} else if (userType == QMetaType::QVariantList) {
|
||||
foreach (const QVariant& element, message.toList()) {
|
||||
handleMessage(element, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) :
|
||||
_foo(foo),
|
||||
_baz(baz),
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QVariantList>
|
||||
|
||||
#include <DatagramSequencer.h>
|
||||
#include <MetavoxelData.h>
|
||||
#include <ScriptCache.h>
|
||||
|
||||
class SequencedTestMessage;
|
||||
|
@ -39,7 +40,9 @@ class Endpoint : public QObject {
|
|||
|
||||
public:
|
||||
|
||||
Endpoint(const QByteArray& datagramHeader);
|
||||
enum Mode { BASIC_PEER_MODE, METAVOXEL_SERVER_MODE, METAVOXEL_CLIENT_MODE };
|
||||
|
||||
Endpoint(const QByteArray& datagramHeader, Mode mode = BASIC_PEER_MODE);
|
||||
|
||||
void setOther(Endpoint* other) { _other = other; }
|
||||
|
||||
|
@ -60,18 +63,26 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
void handleMessage(const QVariant& message, Bitstream& in);
|
||||
|
||||
class SendRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer localState;
|
||||
MetavoxelData data;
|
||||
MetavoxelLOD lod;
|
||||
};
|
||||
|
||||
class ReceiveRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer remoteState;
|
||||
MetavoxelData data;
|
||||
MetavoxelLOD lod;
|
||||
};
|
||||
|
||||
Mode _mode;
|
||||
|
||||
DatagramSequencer* _sequencer;
|
||||
QList<SendRecord> _sendRecords;
|
||||
QList<ReceiveRecord> _receiveRecords;
|
||||
|
@ -79,6 +90,9 @@ private:
|
|||
SharedObjectPointer _localState;
|
||||
SharedObjectPointer _remoteState;
|
||||
|
||||
MetavoxelData _data;
|
||||
MetavoxelLOD _lod;
|
||||
|
||||
Endpoint* _other;
|
||||
QList<QPair<QByteArray, int> > _delayedDatagrams;
|
||||
float _highPriorityMessagesToSend;
|
||||
|
|
Loading…
Reference in a new issue