diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index a7e7844d0b..d10fc87dd5 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -32,11 +32,8 @@ struct GPUKTXPayload { using Version = uint8; static const std::string KEY; - static const Version CURRENT_VERSION { 2 }; - static const size_t PADDING { 2 }; - static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + sizeof(glm::ivec2) + PADDING }; - static_assert(GPUKTXPayload::SIZE == 44, "Packing size may differ between platforms"); - static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned"); + static const Version CURRENT_VERSION { 3 }; + static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint64_t) + sizeof(TextureUsageType) + sizeof(glm::ivec2) }; Sampler::Desc _samplerDesc; Texture::Usage _usage; @@ -44,38 +41,43 @@ struct GPUKTXPayload { glm::ivec2 _originalSize { 0, 0 }; void serialize(DataSerializer &ser) { + ser << CURRENT_VERSION; + + ser << _samplerDesc; - qCWarning(gpulogging) << "Offsets: " << offsetof(struct GPUKTXPayload, _samplerDesc) << offsetof(struct GPUKTXPayload, _usage) << offsetof(struct GPUKTXPayload, _usageType) << offsetof(struct GPUKTXPayload, _originalSize); - uint32 usageData = _usage._flags.to_ulong(); + uint64_t usageData = _usage._flags.to_ulong(); ser << usageData; - - ser << (char)_usageType; + ser << ((uint8_t)_usageType); ser << _originalSize; - ser.addPadding(PADDING); + + // The +1 is here because we're adding the CURRENT_VERSION at the top, but since it's declared as a static + // const, it's not actually part of the class' size. assert(ser.length() == GPUKTXPayload::SIZE); } bool unserialize(DataDeserializer &dsr) { Version version = 0; - uint32 usageData; + uint64_t usageData = 0; uint8_t usagetype = 0; dsr >> version; - if (version > CURRENT_VERSION) { + if (version != CURRENT_VERSION) { // If we try to load a version that we don't know how to parse, // it will render incorrectly - qCWarning(gpulogging) << "KTX version" << version << "is newer than our own," << CURRENT_VERSION; - qCWarning(gpulogging) << "Offsets: " << offsetof(struct GPUKTXPayload, _samplerDesc) << offsetof(struct GPUKTXPayload, _usage) << offsetof(struct GPUKTXPayload, _usageType) << offsetof(struct GPUKTXPayload, _originalSize); + qCWarning(gpulogging) << "KTX version" << version << "is different than our own," << CURRENT_VERSION; qCWarning(gpulogging) << dsr; return false; } dsr >> _samplerDesc; + dsr >> usageData; + _usage._flags = gpu::Texture::Usage::Flags(usageData); + dsr >> usagetype; _usageType = (TextureUsageType)usagetype; diff --git a/libraries/shared/src/SerDes.h b/libraries/shared/src/SerDes.h index 365f14d79c..b0371d4b95 100644 --- a/libraries/shared/src/SerDes.h +++ b/libraries/shared/src/SerDes.h @@ -238,6 +238,33 @@ class DataSerializer { return *this; } + /////////////////////////////////////////////////////////// + + /** + * @brief Add an uint64_t to the output + * + * @param val Value to add + * @return SerDes& This object + */ + DataSerializer &operator<<(uint64_t val) { + return *this << int64_t(val); + } + + /** + * @brief Add an int64_t to the output + * + * @param val Value to add + * @return SerDes& This object + */ + DataSerializer &operator<<(int64_t val) { + if (!extendBy(sizeof(val), "int64_t")) { + return *this; + } + + memcpy(&_store[_pos], (char*)&val, sizeof(val)); + _pos += sizeof(val); + return *this; + } /////////////////////////////////////////////////////////// @@ -410,7 +437,18 @@ class DataSerializer { * @brief Reset the serializer to the start, clear overflow bit. * */ - void rewind() { _pos = 0; _overflow = false; } + void rewind() { _pos = 0; _overflow = false; _lastAdvance = 0; } + + + /** + * @brief Size of the last advance + * + * This can be used to get how many bytes were added in the last operation. + * It is reset on rewind() + * + * @return size_t + */ + size_t lastAdvance() const { return _lastAdvance; } /** * @brief Dump the contents of this object into QDebug @@ -430,7 +468,7 @@ class DataSerializer { if ( _capacity < _length + bytes) { if ( _storeIsExternal ) { - qCritical() << "Serializer trying to write past end of output buffer, writing" << bytes << "bytes for" << type_name << " from position " << _pos << ", length " << _length; + qCritical() << "Serializer trying to write past end of output buffer of" << _capacity << "bytes. Error writing" << bytes << "bytes for" << type_name << " from position " << _pos << ", length " << _length; _overflow = true; return false; } @@ -439,6 +477,7 @@ class DataSerializer { } _length += bytes; + _lastAdvance = bytes; return true; } @@ -451,6 +490,7 @@ class DataSerializer { size_t _capacity = 0; size_t _length = 0; size_t _pos = 0; + size_t _lastAdvance = 0; }; /** @@ -502,6 +542,7 @@ class DataDeserializer { _length = storeLength; _pos = 0; _store = externalStore; + _lastAdvance = 0; } /** @@ -536,6 +577,7 @@ class DataDeserializer { } _pos += bytes; + _lastAdvance = bytes; } @@ -558,6 +600,7 @@ class DataDeserializer { DataDeserializer &operator>>(int8_t &c) { if ( canAdvanceBy(1, "int8_t") ) { c = _store[_pos++]; + _lastAdvance = sizeof(c); } return *this; @@ -585,6 +628,7 @@ class DataDeserializer { if ( canAdvanceBy(sizeof(val), "int16_t") ) { memcpy((char*)&val, &_store[_pos], sizeof(val)); _pos += sizeof(val); + _lastAdvance = sizeof(val); } return *this; @@ -612,10 +656,37 @@ class DataDeserializer { if ( canAdvanceBy(sizeof(val), "int32_t") ) { memcpy((char*)&val, &_store[_pos], sizeof(val)); _pos += sizeof(val); + _lastAdvance = sizeof(val); } return *this; } + /////////////////////////////////////////////////////////// + + /** + * @brief Read an uint64_t from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(uint64_t &val) { + return *this >> reinterpret_cast(val); + } + + /** + * @brief Read an int64_t from the buffer + * + * @param val Value to read + * @return SerDes& This object + */ + DataDeserializer &operator>>(int64_t &val) { + if ( canAdvanceBy(sizeof(val), "int64_t") ) { + memcpy((char*)&val, &_store[_pos], sizeof(val)); + _pos += sizeof(val); + _lastAdvance = sizeof(val); + } + return *this; + } /////////////////////////////////////////////////////////// @@ -630,6 +701,7 @@ class DataDeserializer { if ( canAdvanceBy(sizeof(val), "float") ) { memcpy((char*)&val, &_store[_pos], sizeof(val)); _pos += sizeof(val); + _lastAdvance = sizeof(val); } return *this; } @@ -654,6 +726,7 @@ class DataDeserializer { memcpy((char*)&val.z, &_store[_pos + sz*2], sz); _pos += sz*3; + _lastAdvance = sz * 3; } return *this; @@ -678,6 +751,7 @@ class DataDeserializer { memcpy((char*)&val.w, &_store[_pos + sz*3], sz); _pos += sz*4; + _lastAdvance = sz*4; } return *this; } @@ -699,6 +773,7 @@ class DataDeserializer { memcpy((char*)&val.y, &_store[_pos + sz ], sz); _pos += sz*2; + _lastAdvance = sz * 2; } return *this; @@ -753,7 +828,17 @@ class DataDeserializer { * @brief Reset the serializer to the start, clear overflow bit. * */ - void rewind() { _pos = 0; _overflow = false; } + void rewind() { _pos = 0; _overflow = false; _lastAdvance = 0; } + + /** + * @brief Size of the last advance + * + * This can be used to get how many bytes were added in the last operation. + * It is reset on rewind() + * + * @return size_t + */ + size_t lastAdvance() const { return _lastAdvance; } /** * @brief Dump the contents of this object into QDebug @@ -772,7 +857,7 @@ class DataDeserializer { //qDebug() << "Checking advance by" << bytes; if ( _length < _pos + bytes) { - qCritical() << "Deserializer trying to read past end of input, reading" << bytes << "bytes for" << type_name << "from position " << _pos << ". Max length " << _length; + qCritical() << "Deserializer trying to read past end of input buffer of" << _length << "bytes, reading" << bytes << "bytes for" << type_name << "from position " << _pos; _overflow = true; return false; } @@ -784,4 +869,5 @@ class DataDeserializer { bool _overflow = false; size_t _length = 0; size_t _pos = 0; + size_t _lastAdvance = 0; }; diff --git a/tests/shared/src/SerializerTests.cpp b/tests/shared/src/SerializerTests.cpp index c52a9df69c..ae0198f573 100644 --- a/tests/shared/src/SerializerTests.cpp +++ b/tests/shared/src/SerializerTests.cpp @@ -124,6 +124,35 @@ void SerializerTests::testReadPastEnd() { QCOMPARE(d.pos(), 0); } +void SerializerTests::testWritePastEnd() { + qint8 i8 = 255; + qint16 i16 = 65535; + + + char buf[16]; + + + // 1 byte buffer, we can write 1 byte + memset(buf, 0, sizeof(buf)); + DataSerializer s1(buf, 1); + s1 << i8; + QCOMPARE(s1.pos(), 1); + QCOMPARE(s1.isOverflow(), false); + QCOMPARE(buf[0], i8); + + // 1 byte buffer, we can't write 2 bytes + memset(buf, 0, sizeof(buf)); + DataSerializer s2(buf, 1); + s2 << i16; + QCOMPARE(s2.pos(), 0); + QCOMPARE(s2.isOverflow(), true); + QCOMPARE(buf[0], 0); // We didn't write + QCOMPARE(buf[1], 0); +} + + + + void SerializerTests::benchmarkEncodingDynamicAlloc() { QBENCHMARK { DataSerializer s; diff --git a/tests/shared/src/SerializerTests.h b/tests/shared/src/SerializerTests.h index 3a3a3217d2..55da84c41a 100644 --- a/tests/shared/src/SerializerTests.h +++ b/tests/shared/src/SerializerTests.h @@ -21,6 +21,7 @@ private slots: void testAdd(); void testAddAndRead(); void testReadPastEnd(); + void testWritePastEnd(); void benchmarkEncodingDynamicAlloc(); void benchmarkEncodingStaticAlloc(); void benchmarkDecoding();