diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index d0b77f5..6bd9c09 100644 --- a/source/base/StarAssets.cpp +++ b/source/base/StarAssets.cpp @@ -844,9 +844,14 @@ shared_ptr Assets::loadImage(AssetPath const& path) const { as(loadAsset(AssetId{AssetType::Image, {path.basePath, path.subPath, {}}})); if (!source) return {}; - List operations = path.directives.transformed(imageOperationFromString); StringMap references; - for (auto const& ref : imageOperationReferences(operations)) { + StringList referencePaths; + path.directives.forEach([&](auto const& entry) { + addImageOperationReferences(entry.operation, referencePaths); + }); // TODO: This can definitely be better, was changed quickly to support the new Directives. + + + for (auto const& ref : referencePaths) { auto components = AssetPath::split(ref); validatePath(components, true, false); auto refImage = as(loadAsset(AssetId{AssetType::Image, move(components)})); @@ -857,8 +862,11 @@ shared_ptr Assets::loadImage(AssetPath const& path) const { return unlockDuring([&]() { auto newData = make_shared(); - newData->image = make_shared(processImageOperations( - operations, *source->image, [&](String const& ref) { return references.get(ref).get(); })); + Image newImage = *source->image; + path.directives.forEach([&](auto const& entry) { + processImageOperation(entry.operation, newImage, [&](String const& ref) { return references.get(ref).get(); }); + }); + newData->image = make_shared(move(newImage)); return newData; }); diff --git a/source/core/StarAssetPath.cpp b/source/core/StarAssetPath.cpp index 2100ee0..eb1cd4d 100644 --- a/source/core/StarAssetPath.cpp +++ b/source/core/StarAssetPath.cpp @@ -151,6 +151,18 @@ AssetPath::AssetPath(String const& path) { *this = move(AssetPath::split(path)); // split code should probably be in here, but whatever } +AssetPath::AssetPath(String&& basePath, Maybe&& subPath, DirectivesGroup&& directives) { + this->basePath = move(basePath); + this->subPath = move(subPath); + this->directives = move(directives); +} + +AssetPath::AssetPath(String const& basePath, Maybe const& subPath, DirectivesGroup const& directives) { + this->basePath = basePath; + this->subPath = subPath; + this->directives = directives; +} + std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) { os << rhs.basePath; if (rhs.subPath) { @@ -158,9 +170,9 @@ std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) { os << *rhs.subPath; } - rhs.directives.forEach([&](ImageOperation const& operation, String const& string) { + rhs.directives.forEach([&](auto const& entry) { os << "?"; - os << string; + os << entry.string; }); return os; diff --git a/source/core/StarAssetPath.hpp b/source/core/StarAssetPath.hpp index 9da30e7..3ca3f60 100644 --- a/source/core/StarAssetPath.hpp +++ b/source/core/StarAssetPath.hpp @@ -53,10 +53,11 @@ struct AssetPath { AssetPath() = default; AssetPath(String const& path); - + AssetPath(String&& basePath, Maybe&& subPath, DirectivesGroup&& directives); + AssetPath(const String& basePath, const Maybe& subPath, const DirectivesGroup& directives); String basePath; Maybe subPath; - NestedDirectives directives; + DirectivesGroup directives; bool operator==(AssetPath const& rhs) const; }; diff --git a/source/core/StarDirectives.cpp b/source/core/StarDirectives.cpp index 2394915..766b4d9 100644 --- a/source/core/StarDirectives.cpp +++ b/source/core/StarDirectives.cpp @@ -2,207 +2,133 @@ #include "StarImageProcessing.hpp" #include "StarDirectives.hpp" #include "StarXXHash.hpp" -#include "StarHash.hpp" namespace Star { -NestedDirectives::NestedDirectives() : m_root(nullptr) {} -NestedDirectives::NestedDirectives(String const& directives) { - parseDirectivesIntoLeaf(directives); -} -NestedDirectives::NestedDirectives(String&& directives) { - String mine = move(directives); // most useless move constructor in the world - parseDirectivesIntoLeaf(mine); +Directives::Directives() {} +Directives::Directives(String const& directives) { + parse(directives); } -void NestedDirectives::parseDirectivesIntoLeaf(String const& directives) { - Leaf leaf; - for (String& op : directives.split('?')) { - if (!op.empty()) - leaf.entries.emplace_back(imageOperationFromString(op), op); +Directives::Directives(String&& directives) { + String mine = move(directives); + parse(mine); +} + +void Directives::parse(String const& directives) { + List newList; + StringList split = directives.split('?'); + newList.reserve(split.size()); + for (String& str : split) { + if (!str.empty()) { + ImageOperation operation = imageOperationFromString(str); + newList.emplace_back(move(operation), move(str)); + } } - m_root = std::make_shared(move(leaf)); + entries = std::make_shared const>(move(newList)); + hash = XXH3_64bits(directives.utf8Ptr(), directives.utf8Size()); } -inline bool NestedDirectives::empty() const { - return (bool)m_root; +void Directives::buildString(String& out) const { + for (auto& entry : *entries) { + out += "?"; + out += entry.string; + } } -inline bool NestedDirectives::compare(NestedDirectives const& other) const { - if (m_root == other.m_root) +DirectivesGroup::DirectivesGroup() : m_count(0) {} +DirectivesGroup::DirectivesGroup(String const& directives) { + m_directives.emplace_back(directives); + m_count = m_directives.back().entries->size(); +} +DirectivesGroup::DirectivesGroup(String&& directives) { + m_directives.emplace_back(move(directives)); + m_count = m_directives.back().entries->size(); +} + +inline bool DirectivesGroup::empty() const { + return m_count == 0; +} + +inline bool DirectivesGroup::compare(DirectivesGroup const& other) const { + if (m_count != other.m_count) + return false; + + if (empty()) return true; - return false; + return hash() == other.hash(); } -void NestedDirectives::append(NestedDirectives const& other) { - convertToBranches().emplace_back(other.branch()); +inline bool DirectivesGroup::operator==(DirectivesGroup const& other) const { + return compare(other); } -NestedDirectives& NestedDirectives::operator+=(NestedDirectives const& other) { +inline bool DirectivesGroup::operator!=(DirectivesGroup const& other) const { + return !compare(other); +} + +void DirectivesGroup::append(Directives const& directives) { + m_directives.push_back(directives); + m_count += m_directives.back().entries->size(); +} + +DirectivesGroup& DirectivesGroup::operator+=(Directives const& other) { append(other); return *this; } -String NestedDirectives::toString() const { +inline String DirectivesGroup::toString() const { String string; addToString(string); return string; } -void NestedDirectives::addToString(String& string) const { - if (m_root) - m_root->buildString(string); +void DirectivesGroup::addToString(String& string) const { + for (auto& directives : m_directives) + directives.buildString(string); } -void NestedDirectives::forEach(LeafCallback callback) const { - if (m_root) - m_root->forEach(callback); -} - -void NestedDirectives::forEachPair(LeafPairCallback callback) const { - if (m_root) { - LeafCallback pairCallback = [&](Leaf const& leaf) { - for (auto& entry : leaf.entries) - callback(entry.operation, entry.string); - }; - m_root->forEach(pairCallback); +void DirectivesGroup::forEach(DirectivesCallback callback) const { + for (auto& directives : m_directives) { + for (auto& entry : *directives.entries) + callback(entry); } } -bool NestedDirectives::forEachAbortable(AbortableLeafCallback callback) const { - if (!m_root) - return false; - else - return m_root->forEachAbortable(callback); -} - -bool NestedDirectives::forEachPairAbortable(AbortableLeafPairCallback callback) const { - if (!m_root) - return false; - else { - AbortableLeafCallback pairCallback = [&](Leaf const& leaf) -> bool { - for (auto& entry : leaf.entries) { - if (!callback(entry.operation, entry.string)) - return false; - } - - return true; - }; - return m_root->forEachAbortable(pairCallback); - } -} - -Image NestedDirectives::apply(Image& image) const { - Image current = image; - forEachPair([&](ImageOperation const& operation, String const& string) { - processImageOperation(operation, current); - }); - return current; -} - -NestedDirectives::Branches& NestedDirectives::convertToBranches() { - if (!m_root) { - m_root = std::make_shared(Branches()); - } - else if (m_root->value.is()) - return; - - Leaf& leaf = m_root->value.get(); - Branches newBranches; - newBranches.emplace_back(std::make_shared(move(leaf))); - m_root->value = move(newBranches); - return m_root->value.get(); -} - -bool NestedDirectives::Leaf::Entry::operator==(NestedDirectives::Leaf::Entry const& other) const { - return string == other.string; -} - -bool NestedDirectives::Leaf::Entry::operator!=(NestedDirectives::Leaf::Entry const& other) const { - return string != other.string; -} - - -size_t NestedDirectives::Leaf::length() const { - return entries.size(); -} - -bool NestedDirectives::Leaf::operator==(NestedDirectives::Leaf const& other) const { - size_t len = length(); - if (len != other.length()) - return false; - - for (size_t i = 0; i != len; ++i) { - if (entries[i] != other.entries[i]) - return false; - } - return true; -} - -bool NestedDirectives::Leaf::operator!=(NestedDirectives::Leaf const& other) const { - return !(*this == other); -} - -NestedDirectives::Cell::Cell() : value(Leaf()) {}; -NestedDirectives::Cell::Cell(Leaf&& leaf) : value(move(leaf)) {}; -NestedDirectives::Cell::Cell(Branches&& branches) : value(move(branches)) {}; -NestedDirectives::Cell::Cell(const Leaf& leaf) : value(leaf) {}; -NestedDirectives::Cell::Cell(const Branches& branches) : value(branches) {}; - -/* -bool NestedDirectives::Cell::operator==(NestedDirectives::Cell const& other) const { - if (auto leaf = value.ptr()) { - if (auto otherLeaf = other.value.ptr()) - return *leaf == *otherLeaf; - else { - - } - } - else { - for (auto& branch : value.get()) { - - } - } -} - - -bool NestedDirectives::Cell::operator!=(NestedDirectives::Cell const& other) const { - return !(*this == other); -} -//*/ - -void NestedDirectives::Cell::buildString(String& string) const { - if (auto leaf = value.ptr()) - for (auto& entry : leaf->entries) { - string += "?"; - string += entry.string; - } - else { - for (auto& branch : value.get()) - branch->buildString(string); - } -} - -void NestedDirectives::Cell::forEach(LeafCallback& callback) const { - if (auto leaf = value.ptr()) - callback(*leaf); - else { - for (auto& branch : value.get()) - branch->forEach(callback); - } -} - -bool NestedDirectives::Cell::forEachAbortable(AbortableLeafCallback& callback) const { - if (auto leaf = value.ptr()) { - if (!callback(*leaf)) - return false; - } else { - for (auto& branch : value.get()) - if (!branch->forEachAbortable(callback)) +bool DirectivesGroup::forEachAbortable(AbortableDirectivesCallback callback) const { + for (auto& directives : m_directives) { + for (auto& entry : *directives.entries) { + if (!callback(entry)) return false; + } } + return true; } +inline Image DirectivesGroup::applyNewImage(Image const& image) const { + Image result = image; + applyExistingImage(result); + return result; +} + +void DirectivesGroup::applyExistingImage(Image& image) const { + forEach([&](auto const& entry) { + processImageOperation(entry.operation, image); + }); +} + +inline size_t DirectivesGroup::hash() const { + XXHash3 hasher; + for (auto& directives : m_directives) + hasher.push((const char*)&directives.hash, sizeof(directives.hash)); + + return hasher.digest(); +} + +inline size_t hash::operator()(DirectivesGroup const& s) const { + return s.hash(); +} + } \ No newline at end of file diff --git a/source/core/StarDirectives.hpp b/source/core/StarDirectives.hpp index 924f295..92dfd46 100644 --- a/source/core/StarDirectives.hpp +++ b/source/core/StarDirectives.hpp @@ -2,89 +2,75 @@ #define STAR_DIRECTIVES_HPP #include "StarImageProcessing.hpp" +#include "StarHash.hpp" namespace Star { -STAR_CLASS(NestedDirectives); +STAR_CLASS(DirectivesGroup); STAR_EXCEPTION(DirectivesException, StarException); // Kae: My attempt at reducing memory allocation and per-frame string parsing for extremely long directives -class NestedDirectives { +struct Directives { + struct Entry { + ImageOperation operation; + String string; + + Entry(ImageOperation&& operation, String&& string); + }; + + Directives(); + Directives(String const& directives); + Directives(String&& directives); + + void parse(String const& directives); + + void buildString(String& out) const; + + std::shared_ptr const> entries; + size_t hash = 0; +}; + +class DirectivesGroup { public: - struct Leaf { - struct Entry { - ImageOperation operation; - String string; - - bool operator==(Entry const& other) const; - bool operator!=(Entry const& other) const; - Entry(ImageOperation&& operation, String&& string); - }; - List entries; - - size_t length() const; - bool operator==(NestedDirectives::Leaf const& other) const; - bool operator!=(NestedDirectives::Leaf const& other) const; - }; - - typedef function LeafCallback; - typedef function LeafPairCallback; - typedef function AbortableLeafCallback; - typedef function AbortableLeafPairCallback; - - struct Cell; - typedef std::shared_ptr Branch; - typedef std::shared_ptr ConstBranch; - typedef List Branches; - - - struct Cell { - Variant value; - - Cell(); - Cell(Leaf&& leaf); - Cell(Branches&& branches); - Cell(const Leaf& leaf); - Cell(const Branches& branches); - - void buildString(String& string) const; - void forEach(LeafCallback& callback) const; - bool forEachAbortable(AbortableLeafCallback& callback) const; - }; - - - NestedDirectives(); - NestedDirectives(String const& directives); - NestedDirectives(String&& directives); + DirectivesGroup(); + DirectivesGroup(String const& directives); + DirectivesGroup(String&& directives); void parseDirectivesIntoLeaf(String const& directives); - bool empty() const; - bool compare(NestedDirectives const& other) const; - void append(NestedDirectives const& other); - NestedDirectives& operator+=(NestedDirectives const& other); - bool operator==(NestedDirectives const& other) const; - bool operator!=(NestedDirectives const& other) const; + inline bool empty() const; + bool compare(DirectivesGroup const& other) const; + inline bool operator==(DirectivesGroup const& other) const; + inline bool operator!=(DirectivesGroup const& other) const; + void append(Directives const& other); + DirectivesGroup& operator+=(Directives const& other); - const ConstBranch& branch() const; - - String toString() const; + inline String toString() const; void addToString(String& string) const; - void forEach(LeafCallback callback) const; - void forEachPair(LeafPairCallback callback) const; - bool forEachAbortable(AbortableLeafCallback callback) const; - bool forEachPairAbortable(AbortableLeafPairCallback callback) const; + typedef function DirectivesCallback; + typedef function AbortableDirectivesCallback; - Image apply(Image& image) const; + void forEach(DirectivesCallback callback) const; + bool forEachAbortable(AbortableDirectivesCallback callback) const; + + inline Image applyNewImage(const Image& image) const; + void applyExistingImage(Image& image) const; + + inline size_t hash() const; private: - void buildString(String& string, const Cell& cell) const; - Branches& convertToBranches(); + void buildString(String& string, const DirectivesGroup& directives) const; - Branch m_root; + List m_directives; + size_t m_count; }; -typedef NestedDirectives ImageDirectives; +template <> +struct hash { + size_t operator()(DirectivesGroup const& s) const; +}; + +typedef DirectivesGroup ImageDirectives; } diff --git a/source/core/StarImageProcessing.cpp b/source/core/StarImageProcessing.cpp index fc99f69..c2f980e 100644 --- a/source/core/StarImageProcessing.cpp +++ b/source/core/StarImageProcessing.cpp @@ -352,14 +352,17 @@ String printImageOperations(List const& list) { return StringList(list.transformed(imageOperationToString)).join("?"); } +void addImageOperationReferences(ImageOperation const& operation, StringList& out) { + if (auto op = operation.ptr()) + out.appendAll(op->maskImages); + else if (auto op = operation.ptr()) + out.appendAll(op->blendImages); +} + StringList imageOperationReferences(List const& operations) { StringList references; - for (auto const& operation : operations) { - if (auto op = operation.ptr()) - references.appendAll(op->maskImages); - else if (auto op = operation.ptr()) - references.appendAll(op->blendImages); - } + for (auto const& operation : operations) + addImageOperationReferences(operation, references); return references; } @@ -417,7 +420,7 @@ void processImageOperation(ImageOperation const& operation, Image& image, ImageR } else if (auto op = operation.ptr()) { if (op->maskImages.empty()) - continue; + return; if (!refCallback) throw StarException("Missing image ref callback during AlphaMaskImageOperation in ImageProcessor::process"); @@ -449,7 +452,7 @@ void processImageOperation(ImageOperation const& operation, Image& image, ImageR } else if (auto op = operation.ptr()) { if (op->blendImages.empty()) - continue; + return; if (!refCallback) throw StarException("Missing image ref callback during BlendImageOperation in ImageProcessor::process"); diff --git a/source/core/StarImageProcessing.hpp b/source/core/StarImageProcessing.hpp index ac6151e..30640bf 100644 --- a/source/core/StarImageProcessing.hpp +++ b/source/core/StarImageProcessing.hpp @@ -145,6 +145,8 @@ List parseImageOperations(String const& params); // Each operation separated by '?', returns string with leading '?' String printImageOperations(List const& operations); +void addImageOperationReferences(ImageOperation const& operation, StringList& out); + StringList imageOperationReferences(List const& operations); typedef function ImageReferenceCallback; diff --git a/source/game/StarImageMetadataDatabase.cpp b/source/game/StarImageMetadataDatabase.cpp index 1e5c147..7c619d3 100644 --- a/source/game/StarImageMetadataDatabase.cpp +++ b/source/game/StarImageMetadataDatabase.cpp @@ -122,7 +122,8 @@ String ImageMetadataDatabase::filterProcessing(String const& path) { auto directives = move(components.directives); String joined = AssetPath::join(components); - directives.forEachPair([&](ImageOperation const& operation, String const& string) { + directives.forEach([&](auto const& entry) { + ImageOperation const& operation = entry.operation; if (!(operation.is() || operation.is() || operation.is() || @@ -130,7 +131,7 @@ String ImageMetadataDatabase::filterProcessing(String const& path) { operation.is() || operation.is())) { joined += "?"; - joined += string; + joined += entry.string; } }); @@ -229,14 +230,9 @@ Vec2U ImageMetadataDatabase::calculateImageSize(String const& path) const { OperationSizeAdjust osa(imageSize); - bool complete = components.directives.forEachAbortable([&](auto const& leaf) -> bool { - for (const ImageOperation& operation : leaf.operations) { - operation.call(osa); - if (osa.hasError()) - return false; - else - return true; - } + bool complete = components.directives.forEachAbortable([&](auto const& entry) -> bool { + entry.operation.call(osa); + return !osa.hasError; }); if (!complete)