Skip to content

Commit

Permalink
Formalizing the behavior for using anonymous layer identifiers for fi…
Browse files Browse the repository at this point in the history
…le formats that dynamically generate layer content without a readable resolved asset.

A file format now needs to specify that it can call Read to populate data from an anonymous layer identifier by overriding _ShouldReadAnonymousLayers to return true.
Now the following behaviors exist for layers with formats that ShouldReadAnonymousLayers:
1. FindOrOpen will now attempt to open and read anonymous layers of these formats when it can't find the layer in the registry. This had already worked for file formats
    that could successfully call Read on anonymous layers but it wasn't formalized as to which formats were expected to be able to do this.
2. Layer Reload will now repopulate the layer content through calling Read for anonymous layers when the file format supports it. Previously all anonymous layers whose
    file formats support anonymous reload would always clear the entire contents of the layer (as if it were muted). This now makes sure the reloaded anonymous layer
    will have same contents as when it is first opened.
Added a test case to testSdfLayer to verify our assumptions about how the functions above should work.
Udpated the usdDancingCubesExample to move the data population from file format args out of the InitData and into Read where it belongs. This provides the correct layer muting behavior.
Also updates the dancing cubes example to payload an anonymous layer instead of an empty placeholder file.

Fixes #1356

(Internal change: 2162134)
(Internal change: 2162166)
  • Loading branch information
pixar-oss committed Apr 27, 2021
1 parent d94abf0 commit 4ee62b9
Show file tree
Hide file tree
Showing 19 changed files with 482 additions and 92 deletions.
1 change: 0 additions & 1 deletion extras/usd/examples/usdDancingCubesExample/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pxr_plugin(${PXR_PACKAGE}
RESOURCE_FILES
plugInfo.json
dancingCubes.usda:usdDancingCubesExample/dancingCubes.usda
empty.usddancingcubesexample:usdDancingCubesExample/empty.usddancingcubesexample

DISABLE_PRECOMPILED_HEADERS
)
Expand Down
7 changes: 4 additions & 3 deletions extras/usd/examples/usdDancingCubesExample/dancingCubes.usda
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ def "Root" (
double moveScale = 1.5
token geomType = "Cube"
}
# Payload to the dynamic file. The file must exist but its contents are
# irrelevant as everything is generated from parameters above.
payload = @./empty.usddancingcubesexample@
# Payload to the dynamic file. This can be an anonymous file because
# its contents are completely generated from parameters above and
# the file format allows the reading of anonymous files.
payload = @anon:dummy:cubes.usddancingcubesexample@
)
{
}
22 changes: 11 additions & 11 deletions extras/usd/examples/usdDancingCubesExample/data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,21 +126,27 @@ UsdDancingCubesExample_DataParams::ToArgs() const

/*static*/
UsdDancingCubesExample_DataRefPtr
UsdDancingCubesExample_Data::New(const UsdDancingCubesExample_DataParams &params)
UsdDancingCubesExample_Data::New()
{
return TfCreateRefPtr(new UsdDancingCubesExample_Data(params));
return TfCreateRefPtr(new UsdDancingCubesExample_Data());
}

UsdDancingCubesExample_Data::UsdDancingCubesExample_Data(
const UsdDancingCubesExample_DataParams &params) :
_impl(new UsdDancingCubesExample_DataImpl(params))
UsdDancingCubesExample_Data::UsdDancingCubesExample_Data() :
_impl(new UsdDancingCubesExample_DataImpl())
{
}

UsdDancingCubesExample_Data::~UsdDancingCubesExample_Data()
{
}

void
UsdDancingCubesExample_Data::SetParams(
const UsdDancingCubesExample_DataParams &params)
{
_impl.reset(new UsdDancingCubesExample_DataImpl(params));
}

bool
UsdDancingCubesExample_Data::StreamsData() const
{
Expand All @@ -149,12 +155,6 @@ UsdDancingCubesExample_Data::StreamsData() const
return true;
}

bool
UsdDancingCubesExample_Data::IsEmpty() const
{
return !_impl || _impl->IsEmpty();
}

bool
UsdDancingCubesExample_Data::HasSpec(const SdfPath& path) const
{
Expand Down
10 changes: 4 additions & 6 deletions extras/usd/examples/usdDancingCubesExample/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,15 @@ class UsdDancingCubesExample_Data : public SdfAbstractData
{
public:
/// Factory New. We always create this data with an explicit params object.
static UsdDancingCubesExample_DataRefPtr New(
const UsdDancingCubesExample_DataParams &params);
static UsdDancingCubesExample_DataRefPtr New();

virtual ~UsdDancingCubesExample_Data();

void SetParams(const UsdDancingCubesExample_DataParams &params);

/// SdfAbstractData overrides
bool StreamsData() const override;

bool IsEmpty() const override;

void CreateSpec(const SdfPath& path,
SdfSpecType specType) override;
bool HasSpec(const SdfPath& path) const override;
Expand Down Expand Up @@ -190,8 +189,7 @@ class UsdDancingCubesExample_Data : public SdfAbstractData

private:
// Private constructor for factory New
UsdDancingCubesExample_Data(
const UsdDancingCubesExample_DataParams &params);
UsdDancingCubesExample_Data();

// Pointer to the actual implementation
std::unique_ptr<UsdDancingCubesExample_DataImpl> _impl;
Expand Down
42 changes: 22 additions & 20 deletions extras/usd/examples/usdDancingCubesExample/dataImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,17 @@ static const SdfPath &_GetRootPrimPath()
if (value) { *value = VtValue(val); } \
return true;

UsdDancingCubesExample_DataImpl::UsdDancingCubesExample_DataImpl(
const UsdDancingCubesExample_DataParams &params)
: _params(params)
UsdDancingCubesExample_DataImpl::UsdDancingCubesExample_DataImpl()
{
_params.perSide = 0;
_InitFromParams();
}

bool
UsdDancingCubesExample_DataImpl::IsEmpty() const
UsdDancingCubesExample_DataImpl::UsdDancingCubesExample_DataImpl(
const UsdDancingCubesExample_DataParams &params)
: _params(params)
{
return _primSpecPaths.empty();
_InitFromParams();
}

SdfSpecType
Expand Down Expand Up @@ -139,6 +139,10 @@ bool
UsdDancingCubesExample_DataImpl::Has(
const SdfPath &path, const TfToken &field, VtValue *value) const
{
if (_primSpecPaths.empty()) {
return false;
}

// If property spec, check property fields
if (path.IsPropertyPath()) {

Expand Down Expand Up @@ -175,21 +179,15 @@ UsdDancingCubesExample_DataImpl::Has(
}
// Default prim is always the root prim.
if (field == SdfFieldKeys->DefaultPrim) {
if (path == SdfPath::AbsoluteRootPath() ) {
RETURN_TRUE_WITH_OPTIONAL_VALUE(_GetRootPrimPath().GetNameToken());
}
RETURN_TRUE_WITH_OPTIONAL_VALUE(_GetRootPrimPath().GetNameToken());
}
// Start time code is always 0
if (field == SdfFieldKeys->StartTimeCode) {
if (path == SdfPath::AbsoluteRootPath() ) {
RETURN_TRUE_WITH_OPTIONAL_VALUE(0.0);
}
RETURN_TRUE_WITH_OPTIONAL_VALUE(0.0);
}
// End time code is always num frames - 1
if (field == SdfFieldKeys->EndTimeCode) {
if (path == SdfPath::AbsoluteRootPath() ) {
RETURN_TRUE_WITH_OPTIONAL_VALUE(double(_params.numFrames - 1));
}
RETURN_TRUE_WITH_OPTIONAL_VALUE(double(_params.numFrames - 1));
}

} else {
Expand Down Expand Up @@ -243,7 +241,7 @@ UsdDancingCubesExample_DataImpl::VisitSpecs(
}
}
// Visit the property specs which exist only on leaf prims.
for (auto it : _leafPrimDataMap) {
for (const auto &it : _leafPrimDataMap) {
for (const TfToken &propertyName : _propertyNameTokens->allTokens) {
if (!visitor->VisitSpec(
data, it.first.AppendProperty(propertyName))) {
Expand All @@ -256,6 +254,11 @@ UsdDancingCubesExample_DataImpl::VisitSpecs(
const std::vector<TfToken> &
UsdDancingCubesExample_DataImpl::List(const SdfPath &path) const
{
static std::vector<TfToken> empty;
if (_primSpecPaths.empty()) {
return empty;
}

if (path.IsPropertyPath()) {
// For properties, check that it's a valid leaf prim property
const _LeafPrimPropertyInfo *propInfo =
Expand Down Expand Up @@ -299,7 +302,6 @@ UsdDancingCubesExample_DataImpl::List(const SdfPath &path) const
}
}

static std::vector<TfToken> empty;
return empty;
}

Expand Down Expand Up @@ -418,6 +420,9 @@ UsdDancingCubesExample_DataImpl::_InitFromParams()
return;
}

// Layer always has a root spec that is the default prim of the layer.
_primSpecPaths.insert(_GetRootPrimPath());

// Cache the list of prim child names, numbered 0 to perSide
_primChildNames.resize(_params.perSide);
for (int i = 0; i < _params.perSide; ++ i) {
Expand All @@ -431,9 +436,6 @@ UsdDancingCubesExample_DataImpl::_InitFromParams()
const double frameOffsetAmount =
_params.framesPerCycle / (3.0 * _params.perSide);

// Layer always has a root spec that is the default prim of the layer.
_primSpecPaths.insert(_GetRootPrimPath());

// The layout is a cube of geom prims. We build up each dimension of this
// cube as a hierarchy of child prims.
for (int i = 0; i < _params.perSide; ++i) {
Expand Down
5 changes: 2 additions & 3 deletions extras/usd/examples/usdDancingCubesExample/dataImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,11 @@ PXR_NAMESPACE_OPEN_SCOPE
class UsdDancingCubesExample_DataImpl
{
public:
UsdDancingCubesExample_DataImpl();

UsdDancingCubesExample_DataImpl(
const UsdDancingCubesExample_DataParams &params);

/// Returns true if the parameters produce no specs
bool IsEmpty() const;

/// Generates the spec type for the path.
SdfSpecType GetSpecType(const SdfPath &path) const;

Expand Down

This file was deleted.

35 changes: 31 additions & 4 deletions extras/usd/examples/usdDancingCubesExample/fileFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ SdfAbstractDataRefPtr
UsdDancingCubesExampleFileFormat::InitData(
const FileFormatArguments &args) const
{
// Create our special procedural abstract data with its parameters extracted
// from the file format arguments.
return UsdDancingCubesExample_Data::New(
UsdDancingCubesExample_DataParams::FromArgs(args));
// While we have the file format arguments used to generate the layer
// data here, this function is meant to return the data in its new layer
// form. The Read is responsible for generating the parameter driven
// data. This is important for muting the layer.
return UsdDancingCubesExample_Data::New();
}

bool
Expand All @@ -81,6 +82,15 @@ UsdDancingCubesExampleFileFormat::Read(
return false;
}

// Recreate the procedural abstract data with its parameters extracted
// from the file format arguments.
const FileFormatArguments &args = layer->GetFileFormatArguments();
SdfAbstractDataRefPtr data = InitData(args);
UsdDancingCubesExample_DataRefPtr cubesData =
TfStatic_cast<UsdDancingCubesExample_DataRefPtr>(data);
cubesData->SetParams(UsdDancingCubesExample_DataParams::FromArgs(args));
_SetLayerData(layer, data);

// Enforce that the layer is read only.
layer->SetPermissionToSave(false);
layer->SetPermissionToEdit(false);
Expand Down Expand Up @@ -189,6 +199,23 @@ UsdDancingCubesExampleFileFormat::CanFieldChangeAffectFileFormatArguments(
return false;
}

bool
UsdDancingCubesExampleFileFormat::_ShouldSkipAnonymousReload() const
{
// Anonymous layers need to be reloadable for mute/unmute of the
// layer to work.
return false;
}

bool UsdDancingCubesExampleFileFormat::_ShouldReadAnonymousLayers() const
{
// Anonymous layers of this format are allowed to call Read because
// Read doesn't read from an actual asset path. This allows payloads
// to target anonymous layers of this format.
return true;
}


PXR_NAMESPACE_CLOSE_SCOPE


Expand Down
3 changes: 3 additions & 0 deletions extras/usd/examples/usdDancingCubesExample/fileFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ class UsdDancingCubesExampleFileFormat : public SdfFileFormat,
protected:
SDF_FILE_FORMAT_FACTORY_ACCESS;

bool _ShouldSkipAnonymousReload() const override;
bool _ShouldReadAnonymousLayers() const override;

virtual ~UsdDancingCubesExampleFileFormat();
UsdDancingCubesExampleFileFormat();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,11 @@
# Export the new dynamic layer as usda for baseline comparison.
dynamicLayers[0].Export("newDynamicContents.usda")

# Verify mute and unmute behavior on the dynamic layer itself. A muted layer
# is empty (except the pseudoroot)
premuteLayerString = dynamicLayers[0].ExportToString()
dynamicLayers[0].SetMuted(True)
assert dynamicLayers[0].ExportToString().rstrip() == "#usda 1.0"
dynamicLayers[0].SetMuted(False)
assert dynamicLayers[0].ExportToString() == premuteLayerString

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def "Root" (
}
# Payload to the dynamic file. The file must exist but its contents are
# irrelevant as everything is generated from parameters above.
payload = @./empty.usddancingcubesexample@
payload = @anon:dummy:e.usddancingcubesexample@
)
{
}
11 changes: 11 additions & 0 deletions pxr/usd/sdf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ pxr_test_scripts(
testenv/testSdfTypes.py
)

pxr_build_test_shared_lib(TestSdfNoAssetFileFormat
CREATE_FRAMEWORK
INSTALL_PREFIX SdfPlugins
LIBRARIES
sdf
tf

CPPFILES
testenv/TestSdfNoAssetFileFormat.cpp
)

pxr_build_test(testSdfAttributeBlocking_Cpp
LIBRARIES
sdf
Expand Down
12 changes: 12 additions & 0 deletions pxr/usd/sdf/fileFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ SdfFileFormat::LayersAreFileBased() const
return _LayersAreFileBased();
}

bool
SdfFileFormat::ShouldReadAnonymousLayers() const
{
return _ShouldReadAnonymousLayers();
}

const SdfSchemaBase&
SdfFileFormat::GetSchema() const
{
Expand Down Expand Up @@ -344,6 +350,12 @@ SdfFileFormat::_LayersAreFileBased() const
return true;
}

bool
SdfFileFormat::_ShouldReadAnonymousLayers() const
{
return false;
}

void
SdfFileFormat::_SetLayerData(
SdfLayer* layer,
Expand Down
21 changes: 21 additions & 0 deletions pxr/usd/sdf/fileFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ class SdfFileFormat
/// \sa ArResolver::FetchToLocalResolvedPath
SDF_API bool LayersAreFileBased() const;

/// Returns true if anonymous layer identifiers should be passed to Read
/// when a layer is opened or reloaded.
///
/// Anonymous layers will not have an asset backing and thus for most
/// file formats there is nothing that can be read for an anonymous layer.
/// However, there are file formats that use Read to generate dynamic layer
/// content without reading any data from the resolved asset associated with
/// the layer's identifier.
///
/// For these types of file formats it is useful to be able to open
/// anonymous layers and allow Read to populate them to avoid requiring a
/// placeholder asset to exist just so Read can populate the layer.
SDF_API bool ShouldReadAnonymousLayers() const;

/// Returns true if \p file can be read by this format.
SDF_API
virtual bool CanRead(
Expand Down Expand Up @@ -370,6 +384,13 @@ class SdfFileFormat
SDF_API
virtual bool _LayersAreFileBased() const;

/// File format subclasses may override this to specify whether
/// Read should be called when creating, opening, or reloading an anonymous
/// layer of this format.
/// Default implementation returns false.
SDF_API
virtual bool _ShouldReadAnonymousLayers() const;

const SdfSchemaBase& _schema;
const TfToken _formatId;
const TfToken _target;
Expand Down

0 comments on commit 4ee62b9

Please sign in to comment.