UsdUtilsDependencyInfo
Process (
const SdfLayerRefPtr &,
- const std::string &assetPath,
- const std::vector<std::string> &dependencies,
+ const UsdUtilsDependencyInfo &depInfo,
UsdUtils_DependencyType dependencyType
)
{
- if (dependencies.empty()) {
- PlaceAsset(assetPath, dependencyType);
+ if (depInfo.GetDependencies().empty()) {
+ PlaceAsset(depInfo.GetAssetPath(), dependencyType);
}
else {
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : depInfo.GetDependencies()) {
PlaceAsset(dependency, dependencyType);
}
}
UsdUtils_ReadOnlyLocalizationDelegate delegate(
std::bind(&UsdUtils_ExtractExternalReferencesClient::Process, &client,
std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3, std::placeholders::_4));
+ std::placeholders::_3));
UsdUtils_LocalizationContext context(&delegate);
context.SetRefTypesToInclude(refTypesToInclude);
context.SetRecurseLayerDependencies(false);
return dependencies;
}
+UsdUtilsDependencyInfo
+UsdUtils_ProcessedPathCache::GetProcessedInfo(
+ const SdfLayerRefPtr &layer,
+ const UsdUtilsDependencyInfo &dependencyInfo,
+ UsdUtils_DependencyType dependencyType)
+{
+ auto depKey =
+ std::make_tuple(layer->GetRealPath(), dependencyInfo.GetAssetPath());
+ auto result = _cachedPaths.find(depKey);
+ if (result == _cachedPaths.end()) {
+ UsdUtilsDependencyInfo depInfo = _processingFunc(
+ layer, dependencyInfo, dependencyType);
+
+ _cachedPaths.insert(std::make_pair(depKey, depInfo.GetAssetPath()));
+
+ return depInfo;
+ }
+ else {
+ return UsdUtilsDependencyInfo(result->second);
+ }
+}
+
// Processes sublayer paths, removing duplicates and only updates the paths in
// the writable layer if the processed list differs from the source list.
std::vector<std::string>
for (const std::string& sublayerPath : sublayerPaths) {
UsdUtilsDependencyInfo depInfo(sublayerPath);
- UsdUtilsDependencyInfo info = _processingFunc(
+ UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(
layer, depInfo, UsdUtils_DependencyType::Sublayer);
if (info.GetAssetPath().empty()) {
}
UsdUtilsDependencyInfo depInfo(refOrPayload.GetAssetPath());
- const UsdUtilsDependencyInfo info = _processingFunc(
+ const UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(
layer, depInfo, DEP_TYPE);
if (info.GetAssetPath().empty()) {
const std::vector<std::string> &dependencies)
{
UsdUtilsDependencyInfo depInfo = {authoredPath, dependencies};
- UsdUtilsDependencyInfo info = _processingFunc(
+ UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(
layer, depInfo, UsdUtils_DependencyType::Reference);
const std::string relativeKeyPath = _GetRelativeKeyPath(keyPath);
const std::vector<std::string> &dependencies)
{
UsdUtilsDependencyInfo depInfo = {authoredPath, dependencies};
- const UsdUtilsDependencyInfo info = _processingFunc(
+ const UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(
layer, depInfo, UsdUtils_DependencyType::Reference);
if (!info.GetAssetPath().empty()) {
std::vector<std::string> dependencies)
{
UsdUtilsDependencyInfo depInfo = {templateAssetPath, dependencies};
- const UsdUtilsDependencyInfo info = _processingFunc(layer, depInfo,
- UsdUtils_DependencyType::Reference);
+ const UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(layer,
+ depInfo, UsdUtils_DependencyType::Reference);
if (info.GetAssetPath() == templateAssetPath) {
return _AllDependenciesForInfo(info);
std::vector<std::string> dependencies;
for (const auto &path : layer->GetSubLayerPaths()) {
- UsdUtilsDependencyInfo info = _processingFunc(
- layer, path, {}, UsdUtils_DependencyType::Sublayer);
+ UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(
+ layer, {path, {}}, UsdUtils_DependencyType::Sublayer);
if (info.GetAssetPath().empty()) {
continue;
continue;
}
- UsdUtilsDependencyInfo info = _processingFunc(layer,
- refOrPayload.GetAssetPath(), {}, dependencyType);
+ UsdUtilsDependencyInfo info = _pathCache.GetProcessedInfo(layer,
+ {refOrPayload.GetAssetPath(), {}}, dependencyType);
if (info.GetAssetPath().empty()) {
continue;
const std::string &authoredPath,
const std::vector<std::string> &dependencies)
{
- return _AllDependenciesForInfo(_processingFunc(layer, authoredPath,
- dependencies, UsdUtils_DependencyType::Reference));
-
- return {};
+ return _AllDependenciesForInfo(_pathCache.GetProcessedInfo(layer,
+ {authoredPath, dependencies}, UsdUtils_DependencyType::Reference));
}
std::vector<std::string>
const std::string &authoredPath,
const std::vector<std::string> &dependencies)
{
- return _AllDependenciesForInfo(_processingFunc(layer, authoredPath,
- dependencies, UsdUtils_DependencyType::Reference));
+ return _AllDependenciesForInfo(_pathCache.GetProcessedInfo(layer,
+ {authoredPath, dependencies}, UsdUtils_DependencyType::Reference));
}
std::vector<std::string>
const std::string &templateAssetPath,
std::vector<std::string> dependencies)
{
- return _AllDependenciesForInfo(_processingFunc(
- layer, templateAssetPath, dependencies,
+ return _AllDependenciesForInfo(_pathCache.GetProcessedInfo(
+ layer, {templateAssetPath, dependencies},
UsdUtils_DependencyType::ClipTemplateAssetPath));
}
#include "pxr/usd/usdUtils/dependencies.h"
#include "pxr/usd/usdUtils/userProcessingFunc.h"
+#include "pxr/base/tf/hash.h"
+
#include <vector>
#include <string>
#include <unordered_map>
// context.
struct UsdUtils_LocalizationDelegate
{
+ using ProcessingFunc = std::function<UsdUtilsDependencyInfo(
+ const SdfLayerRefPtr &layer,
+ const UsdUtilsDependencyInfo &dependencyInfo,
+ UsdUtils_DependencyType dependencyType)>;
+
virtual std::vector<std::string> ProcessSublayers(
const SdfLayerRefPtr &layer) { return {}; }
std::vector<std::string> dependencies) { return {}; }
};
+class UsdUtils_ProcessedPathCache {
+public:
+ UsdUtils_ProcessedPathCache(
+ const UsdUtils_LocalizationDelegate::ProcessingFunc &processingFun)
+ : _processingFunc(processingFun) {}
+
+ UsdUtilsDependencyInfo GetProcessedInfo(
+ const SdfLayerRefPtr &layer,
+ const UsdUtilsDependencyInfo &dependencyInfo,
+ UsdUtils_DependencyType dependencyType);
+
+private:
+ using PathKey = std::tuple<std::string, std::string>;
+
+ struct ProcessedPathHash {
+ size_t operator()(const PathKey& key) const
+ {
+ return TfHash::Combine(std::get<0>(key), std::get<1>(key));
+ }
+ };
+
+ std::unordered_map<PathKey, std::string, ProcessedPathHash> _cachedPaths;
+ UsdUtils_LocalizationDelegate::ProcessingFunc _processingFunc;
+};
+
// A Delegate which allows for modification and optional removal of
// asset path values. This delegate invokes a user supplied processing function
// on every asset path it encounters. It will update the path with the returned
: public UsdUtils_LocalizationDelegate
{
public:
- using ProcessingFunc = std::function<UsdUtilsDependencyInfo(
- const SdfLayerRefPtr &layer,
- const UsdUtilsDependencyInfo &dependencyInfo,
- UsdUtils_DependencyType dependencyType)>;
-
UsdUtils_WritableLocalizationDelegate(
ProcessingFunc processingFunc)
- :_processingFunc(processingFunc)
+ : _pathCache(processingFunc)
{}
virtual std::vector<std::string> ProcessSublayers(
static std::string _GetRelativeKeyPath(const std::string& fullPath);
- // the user supplied processing function that will be invoked on every path.
- ProcessingFunc _processingFunc;
+ UsdUtils_ProcessedPathCache _pathCache;
SdfAssetPath _currentValuePath;
VtArray<SdfAssetPath> _currentValuePathArray;
: public UsdUtils_LocalizationDelegate
{
public:
- using ProcessingFunc = std::function<UsdUtilsDependencyInfo(
- const SdfLayerRefPtr &layer,
- const std::string &assetPath,
- const std::vector<std::string> &dependencies,
- UsdUtils_DependencyType dependencyType)>;
-
UsdUtils_ReadOnlyLocalizationDelegate(ProcessingFunc processingFunc)
- : _processingFunc(processingFunc) {}
+ : _pathCache(processingFunc){}
virtual std::vector<std::string> ProcessSublayers(
const SdfLayerRefPtr &layer) override;
const SdfLayerRefPtr &layer,
const std::vector<RefOrPayloadType>& appliedItems);
- ProcessingFunc _processingFunc;
+ UsdUtils_ProcessedPathCache _pathCache;
};
PXR_NAMESPACE_CLOSE_SCOPE
#include "pxr/base/trace/trace.h"
#include <functional>
+#include <unordered_set>
#include <vector>
PXR_NAMESPACE_OPEN_SCOPE
UsdUtilsDependencyInfo
Process(
const SdfLayerRefPtr &layer,
- const std::string & assetPath,
- const std::vector<std::string> &dependencies,
+ const UsdUtilsDependencyInfo &depInfo,
UsdUtils_DependencyType dependencyType)
{
if (processingFunc) {
- UsdUtilsDependencyInfo depInfo = {assetPath, dependencies};
UsdUtilsDependencyInfo processedInfo =
processingFunc(layer, depInfo);
// such as clips or udim, if the user does not modify the
// asset path, we do not want to place it in the resulting arrays
// We always want to add dependencies, however
- bool originalPathIsTemplate = !dependencies.empty();
+ bool originalPathIsTemplate = !depInfo.GetDependencies().empty();
if (processedInfo != depInfo || !originalPathIsTemplate) {
PlaceAsset(layer, processedInfo.GetAssetPath(), dependencyType);
}
return processedInfo;
}
- if (dependencies.empty()) {
- PlaceAsset(layer, assetPath, dependencyType);
+ if (depInfo.GetDependencies().empty()) {
+ PlaceAsset(layer, depInfo.GetAssetPath(), dependencyType);
}
else {
- for (const auto & dependency : dependencies) {
+ for (const auto & dependency : depInfo.GetDependencies()) {
PlaceAsset(layer, dependency, dependencyType);
}
}
if (resolvedPath.empty()) {
if (PathShouldResolve(layer, resolvedPath, dependencyType)) {
- unresolvedPaths.emplace_back(anchoredPath);
+ unresolvedPaths.insert(anchoredPath);
}
}
else if (UsdStage::IsSupportedFile(anchoredPath)) {
- layers.push_back(SdfLayer::FindOrOpen(anchoredPath));
+ layers.insert(SdfLayer::FindOrOpen(anchoredPath));
}
else {
- assets.push_back(resolvedPath);
+ assets.insert(resolvedPath);
}
}
- std::vector<SdfLayerRefPtr> layers;
- std::vector<std::string> assets, unresolvedPaths;
+ std::unordered_set<SdfLayerRefPtr, TfHash> layers;
+ std::unordered_set<std::string> assets, unresolvedPaths;
std::function<UsdUtilsProcessingFunc> processingFunc;
};
UsdUtils_ReadOnlyLocalizationDelegate delegate(
std::bind(&UsdUtils_ComputeAllDependenciesClient::Process, &client,
std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3, std::placeholders::_4));
+ std::placeholders::_3));
UsdUtils_LocalizationContext context(&delegate);
context.SetMetadataFilteringEnabled(true);
- client.layers.emplace_back(rootLayer);
-
if (!context.Process(rootLayer)) {
return false;
}
if (outLayers) {
- *outLayers = std::move(client.layers);
+ outLayers->push_back(rootLayer);
+ outLayers->insert(outLayers->end(),
+ client.layers.begin(), client.layers.end());
+ std::sort(outLayers->begin() + 1, outLayers->end(), [](const SdfLayerRefPtr& a, const SdfLayerRefPtr& b) {
+ return a->GetRealPath() < b->GetRealPath();
+ });
}
if (outAssets) {
- *outAssets = std::move(client.assets);
+ outAssets->assign(client.assets.begin(), client.assets.end());
+ std::sort(outAssets->begin(), outAssets->end());
}
if (outUnresolvedPaths) {
- *outUnresolvedPaths = std::move(client.unresolvedPaths);
+ outUnresolvedPaths->assign(
+ client.unresolvedPaths.begin(), client.unresolvedPaths.end());
+ std::sort(outAssets->begin(), outAssets->end());
}
return true;
Sdf.Layer.Find(layer.identifier),
Sdf.Layer.Find("anon_sublayer.usda")])
self.assertEqual(unresolved, ["unresolved.usda"])
+
+ def test_ComputeAllDependenciesMultipleReferences(self):
+ """Tests that identical paths only appear once in result arrays"""
+
+ def TestDirPath(path):
+ return os.path.normcase(
+ os.path.abspath(os.path.join(testDir, path)))
+
+ testDir = "computeAllDependenciesMultipleReferences"
+ rootLayer = TestDirPath("root.usda")
+ layers, assets, unresolved = \
+ UsdUtils.ComputeAllDependencies(rootLayer)
+
+ self.assertEqual(len(layers), 3)
+ self.assertSetEqual(
+ set(layers),
+ {Sdf.Layer.Find(TestDirPath("common.usda")),
+ Sdf.Layer.Find(TestDirPath("reference.usda")),
+ Sdf.Layer.Find(rootLayer)})
+
+ self.assertEqual(
+ [os.path.normcase(f) for f in assets],
+ [TestDirPath("asset.txt")])
+
+ self.assertEqual(
+ [os.path.normcase(f) for f in unresolved],
+ [TestDirPath("missing.usda")])
def test_ComputeAllDependenciesUserFuncFilterPaths(self):
"""Tests paths that are filtered by the processing func
os.path.normcase(os.path.abspath(assetPathDep))])
self.assertEqual(unresolved, [])
- def test_ComputeAllDependenciesUserFuncModifyPathss(self):
+ def test_ComputeAllDependenciesUserFuncModifyPaths(self):
"""Tests assets paths which are modified by the processing func
appear correctly in returned vectors"""
self.assertEqual([os.path.normcase(f) for f in references],
[os.path.normcase(os.path.abspath("file.txt"))])
self.assertEqual(unresolved, [])
-
-
if __name__=="__main__":
unittest.main()
--- /dev/null
+Text Asset
\ No newline at end of file
--- /dev/null
+#usda 1.0
+(
+ defaultPrim = "common"
+)
+
+def "common" {}
\ No newline at end of file
--- /dev/null
+#usda 1.0
+(
+ defaultPrim = "main"
+)
+
+def "main"
+(
+ references = [@./common.usda@]
+)
+{
+ asset textAsset = @./asset.txt@
+ asset missing = @./missing.usda@
+}
\ No newline at end of file
--- /dev/null
+#usda 1.0
+
+def "Test" (
+ references = [@./reference.usda@, @./common.usda@]
+)
+{
+ asset textAsset = @./asset.txt@
+ asset missing = @./missing.usda@
+}
\ No newline at end of file
with tempfile.TemporaryDirectory() as tempDir:
def TestUserFunc(layer, depInfo):
root, ext = os.path.splitext(depInfo.assetPath)
- if ext != ".usda":
+ if ext != '.usda':
return depInfo
sourceDepLayer = Sdf.Layer.FindOrOpenRelativeToLayer(
layer, depInfo.assetPath)
- crateFileName = root + ".usdc"
+ crateFileName = root + '.usdc'
cratePath = os.path.join(tempDir, crateFileName)
sourceDepLayer.Export(cratePath)
-
return UsdUtils.DependencyInfo(cratePath)
rootPath = 'fileFormatConversion/root.usda'
self._CheckLocalizedPackageContents(localizationDir, expectedFiles)
+ def test_CachedProcessingFuncValues(self):
+ """Tests that the system caches processed asset path values and only
+ invokes the callback once for each layer / path pair"""
+
+ processedPaths = set()
+ def TestUserFunc(layer, depInfo):
+ self.assertTrue(depInfo.assetPath not in processedPaths)
+ processedPaths.add(depInfo.assetPath)
+
+ name, _ = os.path.splitext(os.path.basename(depInfo.assetPath))
+
+ if name.startswith('modify'):
+ return UsdUtils.DependencyInfo('./modified.usda')
+ elif name.startswith('remove'):
+ return UsdUtils.DependencyInfo()
+ else:
+ return depInfo
+
+ rootPath = 'duplicatePaths/root.usda'
+ localizationDir = 'duplicatePaths_localized'
+ localizedRoot = os.path.join(localizationDir, 'root.usda')
+
+ self._Localize(rootPath, localizationDir, TestUserFunc)
+ self.assertIsNotNone(Usd.Stage.Open(localizedRoot))
+
+ self.assertSetEqual(processedPaths,
+ {'./default.usda', './modify.usda', './remove.usda'})
+
+ expectedFiles = [
+ 'default.usda',
+ 'modified.usda',
+ 'root.usda'
+ ]
+
+ self._CheckLocalizedPackageContents(localizationDir, expectedFiles)
+
def _Localize(self, rootPath, localizationDir, userFunc):
if os.path.isdir(localizationDir):
shutil.rmtree(localizationDir)
--- /dev/null
+#usda 1.0
\ No newline at end of file
--- /dev/null
+#usda 1.0
\ No newline at end of file
--- /dev/null
+#usda 1.0
+(
+ defaultPrim = "main"
+ subLayers = [
+ @./default.usda@,
+ @./modify.usda@,
+ @./remove.usda@
+ ]
+)
+
+def "main"
+{
+ asset shouldBeDefault = @./default.usda@
+ asset shouldBeModified = @./modify.usda@
+ asset shouldBeRemoved = @./remove.usda@
+}
\ No newline at end of file