Spanner mutation in test.

This commit is contained in:
Andrzej Kapolka 2014-06-20 18:09:51 -07:00
parent ce778f47b1
commit de0c45a919
10 changed files with 161 additions and 12 deletions

View file

@ -211,6 +211,11 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt
root.writeSubdivision(state);
}
bool Attribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) {
return firstRoot.deepEquals(this, secondRoot, minimum, size, lod);
}
FloatAttribute::FloatAttribute(const QString& name, float defaultValue) :
SimpleInlineAttribute<float>(name, defaultValue) {
}
@ -449,6 +454,12 @@ void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) cons
}
}
bool SharedObjectAttribute::deepEqual(void* first, void* second) const {
SharedObjectPointer firstObject = decodeInline<SharedObjectPointer>(first);
SharedObjectPointer secondObject = decodeInline<SharedObjectPointer>(second);
return firstObject ? firstObject->equals(secondObject) : !secondObject;
}
bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const {
SharedObjectPointer firstChild = decodeInline<SharedObjectPointer>(children[0]);
for (int i = 1; i < MERGE_COUNT; i++) {
@ -489,6 +500,35 @@ MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode(
return new MetavoxelNode(value, original);
}
static bool setsEqual(const SharedObjectSet& firstSet, const SharedObjectSet& secondSet) {
if (firstSet.size() != secondSet.size()) {
return false;
}
// some hackiness here: we assume that the local ids of the first set correspond to the remote ids of the second,
// so that this will work with the tests
foreach (const SharedObjectPointer& firstObject, firstSet) {
int id = firstObject->getID();
bool found = false;
foreach (const SharedObjectPointer& secondObject, secondSet) {
if (secondObject->getRemoteID() == id) {
if (!firstObject->equals(secondObject)) {
return false;
}
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
bool SharedObjectSetAttribute::deepEqual(void* first, void* second) const {
return setsEqual(decodeInline<SharedObjectSet>(first), decodeInline<SharedObjectSet>(second));
}
bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const {
for (int i = 0; i < MERGE_COUNT; i++) {
if (!decodeInline<SharedObjectSet>(children[i]).isEmpty()) {
@ -563,3 +603,12 @@ void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, M
state.stream << SharedObjectPointer();
}
bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) {
SharedObjectSet firstSet;
firstRoot.getSpanners(this, minimum, size, lod, firstSet);
SharedObjectSet secondSet;
secondRoot.getSpanners(this, minimum, size, lod, secondSet);
return setsEqual(firstSet, secondSet);
}

View file

@ -27,6 +27,7 @@ class QScriptValue;
class Attribute;
class MetavoxelData;
class MetavoxelLOD;
class MetavoxelNode;
class MetavoxelStreamState;
@ -213,6 +214,11 @@ public:
virtual bool equal(void* first, void* second) const = 0;
virtual bool deepEqual(void* first, void* second) const { return equal(first, second); }
virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod);
/// Merges the value of a parent and its children.
/// \param postRead whether or not the merge is happening after a read
/// \return whether or not the children and parent values are all equal
@ -406,6 +412,8 @@ public:
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual bool deepEqual(void* first, void* second) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual void* createFromVariant(const QVariant& value) const;
@ -434,6 +442,8 @@ public:
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual bool deepEqual(void* first, void* second) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
@ -462,6 +472,9 @@ public:
virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state);
virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state);
virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod);
};
#endif // hifi_AttributeRegistry_h

View file

@ -110,6 +110,10 @@ const TypeStreamer* Bitstream::getTypeStreamer(int type) {
return getTypeStreamers().value(type);
}
const ObjectStreamer* Bitstream::getObjectStreamer(const QMetaObject* metaObject) {
return getObjectStreamers().value(metaObject);
}
const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) {
return getMetaObjects().value(className);
}
@ -2316,6 +2320,15 @@ QObject* MappedObjectStreamer::putJSONData(JSONReader& reader, const QJsonObject
return object;
}
bool MappedObjectStreamer::equal(const QObject* first, const QObject* second) const {
foreach (const StreamerPropertyPair& property, _properties) {
if (!property.first->equal(property.second.read(first), property.second.read(second))) {
return false;
}
}
return true;
}
void MappedObjectStreamer::write(Bitstream& out, const QObject* object) const {
foreach (const StreamerPropertyPair& property, _properties) {
property.first->write(out, property.second.read(object));
@ -2433,6 +2446,17 @@ QObject* GenericObjectStreamer::putJSONData(JSONReader& reader, const QJsonObjec
return object;
}
bool GenericObjectStreamer::equal(const QObject* first, const QObject* second) const {
const QVariantList& firstValues = static_cast<const GenericSharedObject*>(first)->getValues();
const QVariantList& secondValues = static_cast<const GenericSharedObject*>(second)->getValues();
for (int i = 0; i < _properties.size(); i++) {
if (!_properties.at(i).first->equal(firstValues.at(i), secondValues.at(i))) {
return false;
}
}
return true;
}
void GenericObjectStreamer::write(Bitstream& out, const QObject* object) const {
const QVariantList& values = static_cast<const GenericSharedObject*>(object)->getValues();
for (int i = 0; i < _properties.size(); i++) {

View file

@ -278,6 +278,9 @@ public:
/// Returns the streamer registered for the supplied type, if any.
static const TypeStreamer* getTypeStreamer(int type);
/// Returns the streamer registered for the supplied object, if any.
static const ObjectStreamer* getObjectStreamer(const QMetaObject* metaObject);
/// Returns the meta-object registered under the supplied class name, if any.
static const QMetaObject* getMetaObject(const QByteArray& className);
@ -1022,6 +1025,7 @@ public:
virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const = 0;
virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const = 0;
virtual bool equal(const QObject* first, const QObject* second) const = 0;
virtual void write(Bitstream& out, const QObject* object) const = 0;
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0;
virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0;
@ -1047,6 +1051,7 @@ public:
virtual QJsonObject getJSONMetadata(JSONWriter& writer) const;
virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const;
virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const;
virtual bool equal(const QObject* first, const QObject* second) const;
virtual void write(Bitstream& out, const QObject* object) const;
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
virtual QObject* read(Bitstream& in, QObject* object = NULL) const;
@ -1070,6 +1075,7 @@ public:
virtual QJsonObject getJSONMetadata(JSONWriter& writer) const;
virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const;
virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const;
virtual bool equal(const QObject* first, const QObject* second) const;
virtual void write(Bitstream& out, const QObject* object) const;
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
virtual QObject* read(Bitstream& in, QObject* object = NULL) const;

View file

@ -697,7 +697,7 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l
glm::vec3 minimum = getMinimum();
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
MetavoxelNode* otherNode = other._roots.value(it.key());
if (!(otherNode && it.value()->deepEquals(it.key(), *otherNode, minimum, _size, lod))) {
if (!(otherNode && it.key()->metavoxelRootsEqual(*it.value(), *otherNode, minimum, _size, lod))) {
return false;
}
}
@ -1102,7 +1102,7 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) {
bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const MetavoxelNode& other,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const {
if (!attribute->equal(_attributeValue, other._attributeValue)) {
if (!attribute->deepEqual(_attributeValue, other._attributeValue)) {
return false;
}
if (!lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) {
@ -1125,6 +1125,19 @@ bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const Metavoxe
return true;
}
void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::vec3& minimum,
float size, const MetavoxelLOD& lod, SharedObjectSet& results) const {
results.unite(decodeInline<SharedObjectSet>(_attributeValue));
if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) {
return;
}
float nextSize = size * 0.5f;
for (int i = 0; i < CHILD_COUNT; i++) {
glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i);
_children[i]->getSpanners(attribute, nextMinimum, nextSize, lod, results);
}
}
int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth,
int fifth, int sixth, int seventh, int eighth) {
return first | (second << 3) | (third << 6) | (fourth << 9) |

View file

@ -221,6 +221,10 @@ public:
/// Performs a deep comparison between this and the specified other node.
bool deepEquals(const AttributePointer& attribute, const MetavoxelNode& other,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const;
/// Retrieves all spanners satisfying the LOD constraint, placing them in the provided set.
void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum,
float size, const MetavoxelLOD& lod, SharedObjectSet& results) const;
private:
Q_DISABLE_COPY(MetavoxelNode)

View file

@ -84,11 +84,19 @@ bool SharedObject::equals(const SharedObject* other, bool sharedAncestry) const
if (metaObject != other->metaObject() && !sharedAncestry) {
return false;
}
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (property.isStored() && property.read(this) != property.read(other)) {
// use the streamer, if we have one
const ObjectStreamer* streamer = Bitstream::getObjectStreamer(metaObject);
if (streamer) {
if (!streamer->equal(this, other)) {
return false;
}
} else {
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (property.isStored() && property.read(this) != property.read(other)) {
return false;
}
}
}
QList<QByteArray> dynamicPropertyNames = this->dynamicPropertyNames();
if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) {

View file

@ -68,7 +68,7 @@ float randFloat();
int randIntInRange (int min, int max);
float randFloatInRange (float min,float max);
float randomSign(); /// \return -1.0 or 1.0
unsigned char randomColorValue(int minimum);
unsigned char randomColorValue(int minimum = 0);
bool randomBoolean();
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);

View file

@ -47,6 +47,8 @@ static int sharedObjectsDestroyed = 0;
static int objectMutationsPerformed = 0;
static int scriptObjectsCreated = 0;
static int scriptMutationsPerformed = 0;
static int metavoxelMutationsPerformed = 0;
static int spannerMutationsPerformed = 0;
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
@ -395,6 +397,8 @@ bool MetavoxelTests::run() {
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
datagramsReceived << "with" << bytesReceived << "bytes";
qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet";
qDebug() << "Performed" << metavoxelMutationsPerformed << "metavoxel mutations," << spannerMutationsPerformed <<
"spanner mutations";
}
qDebug() << "All tests passed!";
@ -472,6 +476,12 @@ Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) :
RandomVisitor visitor;
_data.guide(visitor);
qDebug() << "Created" << visitor.leafCount << "base leaves";
_data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), new Sphere());
_sphere = new Sphere();
static_cast<Transformable*>(_sphere.data())->setScale(0.01f);
_data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), _sphere);
return;
}
// create the object that represents out delta-encoded state
@ -614,9 +624,10 @@ int MutateVisitor::visit(MetavoxelInfo& info) {
if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) {
return encodeRandomOrder();
}
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<QRgb>(qRgb(randIntInRange(0, 255),
randIntInRange(0, 255), randIntInRange(0, 255))));
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<QRgb>(qRgb(randomColorValue(),
randomColorValue(), randomColorValue())));
_mutationsRemaining--;
metavoxelMutationsPerformed++;
return STOP_RECURSION;
}
@ -651,6 +662,21 @@ bool Endpoint::simulate(int iterationNumber) {
MutateVisitor visitor;
_data.guide(visitor);
// perhaps mutate the spanner
if (randomBoolean()) {
SharedObjectPointer oldSphere = _sphere;
_sphere = _sphere->clone(true);
Sphere* newSphere = static_cast<Sphere*>(_sphere.data());
if (randomBoolean()) {
newSphere->setColor(QColor(randomColorValue(), randomColorValue(), randomColorValue()));
} else {
newSphere->setTranslation(newSphere->getTranslation() + glm::vec3(randFloatInRange(-0.01f, 0.01f),
randFloatInRange(-0.01f, 0.01f), randFloatInRange(-0.01f, 0.01f)));
}
_data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), oldSphere, _sphere);
spannerMutationsPerformed++;
}
// wait until we have a valid lod before sending
if (!_lod.isValid()) {
return false;
@ -772,7 +798,8 @@ void Endpoint::readMessage(Bitstream& in) {
foreach (const SendRecord& sendRecord, _other->_sendRecords) {
if (sendRecord.packetNumber == packetNumber) {
if (!sendRecord.data.deepEquals(_data, _sendRecords.first().lod)) {
throw QString("Sent/received metavoxel data mismatch.");
qDebug() << "Sent/received metavoxel data mismatch.";
exit(true);
}
break;
}
@ -807,17 +834,20 @@ void Endpoint::readMessage(Bitstream& in) {
it != _other->_unreliableMessagesSent.end(); it++) {
if (it->sequenceNumber == message.sequenceNumber) {
if (!messagesEqual(it->submessage, message.submessage)) {
throw QString("Sent/received unreliable message mismatch.");
qDebug() << "Sent/received unreliable message mismatch.";
exit(true);
}
if (!it->state->equals(message.state)) {
throw QString("Delta-encoded object mismatch.");
qDebug() << "Delta-encoded object mismatch.";
exit(true);
}
_other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1);
unreliableMessagesReceived++;
return;
}
}
throw QString("Received unsent/already sent unreliable message.");
qDebug() << "Received unsent/already sent unreliable message.";
exit(true);
}
void Endpoint::handleReliableMessage(const QVariant& message) {

View file

@ -93,6 +93,8 @@ private:
MetavoxelData _data;
MetavoxelLOD _lod;
SharedObjectPointer _sphere;
Endpoint* _other;
QList<QPair<QByteArray, int> > _delayedDatagrams;
float _highPriorityMessagesToSend;