From b87ad96235a1b9f7d97d6433d1cbbf449eea0454 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Sat, 1 Jun 2024 23:27:23 -0400 Subject: [PATCH] feat: reduce unnecessary copies of vectors --- include/vtfpp/vtfpp.h | 23 +++++++++------ src/thirdparty/bufferstream | 2 +- src/vtfpp/ImageConversion.cpp | 20 ++++++------- src/vtfpp/vtfpp.cpp | 55 +++++++++++++++++------------------ test/vtfpp.cpp | 2 +- 5 files changed, 52 insertions(+), 50 deletions(-) diff --git a/include/vtfpp/vtfpp.h b/include/vtfpp/vtfpp.h index 6ccd6eec0..ccb75db2c 100644 --- a/include/vtfpp/vtfpp.h +++ b/include/vtfpp/vtfpp.h @@ -19,6 +19,7 @@ constexpr uint32_t VTF_SIGNATURE = 'V' + ('T' << 8) + ('F' << 16); struct Resource { enum Type : uint8_t { + TYPE_UNKNOWN = '\0', // Unknown TYPE_THUMBNAIL_DATA = '\x01', // \x01\0\0 TYPE_IMAGE_DATA = '\x30', // \x30\0\0 TYPE_PARTICLE_SHEET_DATA = '\x10', // \x10\0\0 @@ -34,15 +35,15 @@ struct Resource { FLAG_NO_DATA = 1 << 1, }; - Type type; - Flags flags; - std::vector data; + Type type = TYPE_UNKNOWN; + Flags flags = FLAG_NONE; + std::span data; using ConvertedData = std::variant< std::monostate, // Anything that would be equivalent to just returning data directly, or used as an error uint32_t, // CRC, TSO std::pair, // LOD - std::string_view // KVD + std::string // KVD >; [[nodiscard]] ConvertedData convertData() const; @@ -58,8 +59,8 @@ struct Resource { return std::get>(this->convertData()); } - [[nodiscard]] std::string_view getDataAsKeyValuesData() const { - return std::get(this->convertData()); + [[nodiscard]] std::string getDataAsKeyValuesData() const { + return std::get(this->convertData()); } }; @@ -102,14 +103,16 @@ class VTF { FLAG_SPECVAR_ALPHA = 1 << 31, // Removed }; - VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false); - - VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false); + explicit VTF(std::vector&& vtfData, bool parseHeaderOnly = false); explicit VTF(const std::vector& vtfData, bool parseHeaderOnly = false); explicit VTF(const std::vector& vtfData, bool parseHeaderOnly = false); + VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false); + + VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false); + [[nodiscard]] explicit operator bool() const; [[nodiscard]] uint32_t getMajorVersion() const; @@ -167,6 +170,8 @@ class VTF { private: bool opened = false; + std::vector data; + //uint32_t signature; uint32_t majorVersion{}; uint32_t minorVersion{}; diff --git a/src/thirdparty/bufferstream b/src/thirdparty/bufferstream index 861043814..4741d64b9 160000 --- a/src/thirdparty/bufferstream +++ b/src/thirdparty/bufferstream @@ -1 +1 @@ -Subproject commit 8610438144437c11d8dc79b04c04ba76e7535973 +Subproject commit 4741d64b900830e1dc1bea12c325b618894a1fd7 diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 28f2e81ee..4efb1994d 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -76,7 +76,7 @@ namespace { if (CMP_ConvertTexture(&srcTexture, &destTexture, &options, nullptr) != CMP_OK) { return {}; } - return destData; + return std::move(destData); } [[nodiscard]] std::vector convertImageDataToRGBA8888(std::span imageData, ImageFormat format) { @@ -241,7 +241,7 @@ namespace { default: break; } - return newData; + return std::move(newData); } [[nodiscard]] std::vector convertImageDataFromRGBA8888(std::span imageData, ImageFormat format) { @@ -413,15 +413,15 @@ namespace { default: break; } - return newData; + return std::move(newData); } [[nodiscard]] std::vector decodeImageDataToRGBA8888(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, format, ImageFormat::RGBA8888, width, height); + return std::move(::convertImageDataUsingCompressonator(imageData, format, ImageFormat::RGBA8888, width, height)); } [[nodiscard]] std::vector encodeImageDataFromRGBA8888(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, format, width, height); + return std::move(::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, format, width, height)); } [[nodiscard]] std::vector convertImageDataToRGBA32323232F(std::span imageData, ImageFormat format) { @@ -473,15 +473,15 @@ std::vector ImageConversion::convertImageDataToFormat(std::span ImageConversion::convertImageDataToFile(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { @@ -498,5 +498,5 @@ std::vector ImageConversion::convertImageDataToFile(std::spandata.size() <= 4) { - return std::string_view{""}; + return ""; } - return std::string_view{reinterpret_cast(this->data.data() + 4), *reinterpret_cast(this->data.data()) + 1}; + return std::string(reinterpret_cast(this->data.data() + 4), *reinterpret_cast(this->data.data())); default: break; } return {}; } -VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) { - BufferStreamReadOnly stream{vtfData, vtfSize}; +VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) + : data(std::move(vtfData)) { + BufferStreamReadOnly stream{this->data}; if (stream.read() != VTF_SIGNATURE) { return; @@ -94,7 +95,7 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) { .read(resource->type) .skip(2) .read(resource->flags); - resource->data = stream.read_bytes(4); + resource->data = stream.read_span(4); if (!(resource->flags & Resource::FLAG_NO_DATA)) { if (lastResourceIndex >= 0) { @@ -103,7 +104,7 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) { auto curPos = stream.tell(); stream.seek(lastOffset); - this->resources[lastResourceIndex]->data = stream.read_bytes(currentOffset - lastOffset); + this->resources[lastResourceIndex]->data = stream.read_span(currentOffset - lastOffset); stream.seek(curPos); } lastResourceIndex = static_cast(this->resources.size() - 1); @@ -113,17 +114,10 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) { auto offset = *reinterpret_cast(this->resources[lastResourceIndex]->data.data()); auto curPos = stream.tell(); stream.seek(offset); - this->resources[lastResourceIndex]->data = stream.read_bytes(stream.size() - offset); + this->resources[lastResourceIndex]->data = stream.read_span(stream.size() - offset); stream.seek(curPos); } - // Null-terminate KeyValues data so std::string_view works - for (auto& resource : this->resources) { - if (resource->type == Resource::TYPE_KEYVALUES_DATA) { - resource->data.push_back(std::byte{'\0'}); - } - } - this->opened = stream.tell() == headerLength; } else { while (stream.tell() % 16 != 0) { @@ -133,23 +127,26 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) { if (this->thumbnailWidth > 0 && this->thumbnailHeight > 0) { auto thumbnailLength = ImageFormatDetails::getDataLength(this->thumbnailFormat, this->thumbnailWidth, this->thumbnailHeight); - this->resources.emplace_back(new Resource{Resource::TYPE_THUMBNAIL_DATA, Resource::FLAG_NONE, stream.read_bytes(thumbnailLength)}); + this->resources.emplace_back(new Resource{Resource::TYPE_THUMBNAIL_DATA, Resource::FLAG_NONE, stream.read_span(thumbnailLength)}); } if (this->width > 0 && this->height > 0) { auto imageLength = ImageFormatDetails::getDataLength(this->format, this->mipCount, this->frameCount, this->getFaceCount(), this->width, this->height, this->sliceCount); - this->resources.emplace_back(new Resource{Resource::TYPE_IMAGE_DATA, Resource::FLAG_NONE, stream.read_bytes(imageLength)}); + this->resources.emplace_back(new Resource{Resource::TYPE_IMAGE_DATA, Resource::FLAG_NONE, stream.read_span(imageLength)}); } } } -VTF::VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly) - : VTF(reinterpret_cast(vtfData), vtfSize, parseHeaderOnly) {} - VTF::VTF(const std::vector& vtfData, bool parseHeaderOnly) - : VTF(vtfData.data(), vtfData.size(), parseHeaderOnly) {} + : VTF(std::vector{vtfData.begin(), vtfData.end()}, parseHeaderOnly) {} VTF::VTF(const std::vector& vtfData, bool parseHeaderOnly) - : VTF(vtfData.data(), vtfData.size(), parseHeaderOnly) {} + : VTF(std::vector{reinterpret_cast(vtfData.data()), reinterpret_cast(vtfData.data() + vtfData.size())}, parseHeaderOnly) {} + +VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) + : VTF(std::vector{vtfData, vtfData + vtfSize}, parseHeaderOnly) {} + +VTF::VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly) + : VTF(std::vector{reinterpret_cast(vtfData), reinterpret_cast(vtfData + vtfSize)}, parseHeaderOnly) {} VTF::operator bool() const { return this->opened; @@ -238,7 +235,7 @@ std::span VTF::getImageData(uint8_t mip, uint16_t frame, uint16 if (!ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->mipCount, frame, this->frameCount, face, this->getFaceCount(), this->width, this->height, slice, this->sliceCount)) { return {}; } - return std::span{imageResource->data}.subspan(offset, length); + return imageResource->data.subspan(offset, length); } return {}; } @@ -248,11 +245,11 @@ std::vector VTF::getImageDataAs(ImageFormat newFormat, uint8_t mip, u if (rawImageData.empty()) { return {}; } - return ImageConversion::convertImageDataToFormat(rawImageData, this->format, newFormat, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height)); + return std::move(ImageConversion::convertImageDataToFormat(rawImageData, this->format, newFormat, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height))); } std::vector VTF::getImageDataAsRGBA8888(uint8_t mip, uint16_t frame, uint16_t face, uint16_t slice) const { - return this->getImageDataAs(ImageFormat::RGBA8888, mip, frame, face, slice); + return std::move(this->getImageDataAs(ImageFormat::RGBA8888, mip, frame, face, slice)); } std::vector VTF::convertAndSaveImageDataToFile(uint8_t mip, uint16_t frame, uint16_t face, uint16_t slice) const { @@ -260,12 +257,12 @@ std::vector VTF::convertAndSaveImageDataToFile(uint8_t mip, uint16_t if (rawImageData.empty()) { return {}; } - return ImageConversion::convertImageDataToFile(rawImageData, this->format, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height)); + return std::move(ImageConversion::convertImageDataToFile(rawImageData, this->format, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height))); } std::span VTF::getThumbnailData() const { if (auto thumbnailResource = this->getResource(Resource::TYPE_THUMBNAIL_DATA)) { - return {thumbnailResource->data}; + return thumbnailResource->data; } return {}; } @@ -275,11 +272,11 @@ std::vector VTF::getThumbnailDataAs(ImageFormat newFormat) const { if (rawThumbnailData.empty()) { return {}; } - return ImageConversion::convertImageDataToFormat(rawThumbnailData, this->thumbnailFormat, newFormat, this->thumbnailWidth, this->thumbnailHeight); + return std::move(ImageConversion::convertImageDataToFormat(rawThumbnailData, this->thumbnailFormat, newFormat, this->thumbnailWidth, this->thumbnailHeight)); } std::vector VTF::getThumbnailDataAsRGBA8888() const { - return this->getThumbnailDataAs(ImageFormat::RGBA8888); + return std::move(this->getThumbnailDataAs(ImageFormat::RGBA8888)); } std::vector VTF::convertAndSaveThumbnailDataToFile() const { @@ -287,5 +284,5 @@ std::vector VTF::convertAndSaveThumbnailDataToFile() const { if (rawThumbnailData.empty()) { return {}; } - return ImageConversion::convertImageDataToFile(rawThumbnailData, this->thumbnailFormat, this->thumbnailWidth, this->thumbnailHeight); + return std::move(ImageConversion::convertImageDataToFile(rawThumbnailData, this->thumbnailFormat, this->thumbnailWidth, this->thumbnailHeight)); } diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index 75c2881b9..bdbc7b4eb 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -970,7 +970,7 @@ TEST(vtfpp, read_v75) { ASSERT_TRUE(keyValues); EXPECT_EQ(keyValues->flags, Resource::FLAG_NONE); auto keyValuesData = keyValues->getDataAsKeyValuesData(); - EXPECT_STREQ(keyValuesData.data(), "\"Information\"\r\n{\r\n\t\"Author\" \"craftablescience\"\r\n\t\"Contact\" \"contact\"\r\n\t\"Version\" \"version\"\r\n\t\"Modification\" \"modification\"\r\n\t\"Description\" \"description\"\r\n\t\"Comments\" \"comments\"\r\n}\r\n"); + EXPECT_STREQ(keyValuesData.c_str(), "\"Information\"\r\n{\r\n\t\"Author\" \"craftablescience\"\r\n\t\"Contact\" \"contact\"\r\n\t\"Version\" \"version\"\r\n\t\"Modification\" \"modification\"\r\n\t\"Description\" \"description\"\r\n\t\"Comments\" \"comments\"\r\n}\r\n"); } TEST(vtfpp, read_v75_nomip) {