Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial implementation of usdSemantics #3103

Merged
merged 1 commit into from
Oct 3, 2024

Conversation

nvmkuruc
Copy link
Collaborator

@nvmkuruc nvmkuruc commented May 31, 2024

Description of Change(s)

Implementation of https://github.com/PixarAnimationStudios/OpenUSD-proposals/tree/main/proposals/semantic_schema which adds a SemanticsLabelsAPI schema for labeling prims with tokens.

Note that the implementation diverges slightly with the proposal with the taxonomy being after labels instead of before in the property name. (ie. semantics:labels:<taxonomy> instead of semantics:<taxonomy>:labels).

In addition to the schema, a utility UsdSemanticsLabelsQuery provides a reference for how ancestor labeling is expected to work and inherit.

Fixes Issue(s)

  • I have verified that all unit tests pass with the proposed changes
  • I have submitted a signed Contributor License Agreement

@jesschimein
Copy link
Contributor

Filed as internal issue #USD-9719

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pixar-oss
Copy link
Member

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@nvmkuruc nvmkuruc force-pushed the semantics branch 6 times, most recently from 02d4fcd to 89624aa Compare August 9, 2024 14:12
@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Comment on lines 74 to 75
/// Compute the union of values for `semantics:labels:<taxonomy>` in the
/// specified interval.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this and other methods that return an array of labels, please specify whether there is a stable ordering of the returned values, or client must expect them to be disordered.

For this method specifically, it WBN to just reinforce the convention that "Direct" means this prim's contributions only.


bool
UsdSemanticsLabelsQuery::_PopulateLabels(const UsdPrim& prim) {
TF_DEV_AXIOM(!prim.IsPseudoRoot());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TF_VERIFY is preferred to TF_DEV_AXIOM

UsdSemanticsLabelsQuery::_PopulateLabels(const UsdPrim& prim) {
TF_DEV_AXIOM(!prim.IsPseudoRoot());
UsdSemanticsLabelsAPI schema(prim, _taxonomy);
if (!schema.GetPrim().HasAPI<decltype(schema)>(schema.GetName()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructing a schema object is not trivial in cost; can we do this check first, using the given prim, UsdSemanticsLabelsAPI, and _taxonomy ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be doing this check at all-- we should just be validating the schema.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HasAPI check is a faster check, though, and given that we expect, in many scenes, to have few if any applications of the schema, it may be worth doing the fast check up front?

Comment on lines 49 to 50
/// The query caches certain reads and computations and should be discarded
/// when the state of the stage changes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helpful to mention that it is safe to use in a multi-threaded context... not all caches in Usd schemas are currently, regrettably.


class "SemanticsLabelsAPI" (
inherits = </APISchemaBase>
doc = """Applied set of labels for a prim for a taxonomy specified by the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given implied meanings of "set", can we be specific on what we mean, here? Is it an ordered list of labels, or is authored order insignificant?

pxr/usd/usdSemantics/labelsQuery.h Show resolved Hide resolved
@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

@matthewcpp matthewcpp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments contained herein came from our internal code review.

return {std::cbegin(labelsAtTime), std::cend(labelsAtTime)};
}
if constexpr (std::is_same_v<TimeType, GfInterval>) {
TF_DEV_AXIOM(!queryTime.IsEmpty());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this could use TF_VERIFY? Unless checking this condition is expensive enough to have a real impact on performance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Shouldn't be performance critical.

VtTokenArray labelsAtTime;
if (!labelsAttr.Get(&labelsAtTime, timeInInterval)) {
TF_RUNTIME_ERROR("Failed to read value at %s",
UsdDescribe(labelsAttr).c_str());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious when we would hit the two conditions here that cause runtime errors to be emitted. Are those really runtime conditions that make that happen?

(Sorry to be so focused on error policy here, rather than what I'm supposed to be weighing in on!!)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing so, @nvmkuruc , as I was just about to. RUNTIME_ERRORs should be reserved for system-level errors. Here and elsewhere, if we really expected there should have been something authored but there wasn't, it would be more of a TF_WARN, I think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthewcpp The following layer can trigger that runtime error

def "test" (apiSchemas = ["SemanticsLabelsAPI:test"])
{
    int[] semantics:labels:test.timeSamples = {100: [1]}
}

with the following code.

from pxr import Usd, UsdSemantics
s = Usd.Stage.Open('sl.usda')
query = UsdSemantics.LabelsQuery('test', 100)
print(query.ComputeUniqueDirectLabels(s.GetPrimAtPath('/test')))

@spiffmon I'm happy to use whatever diagnostic makes the most sense, but it did seem like an error to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so that's "bad scene description", similar to having something like

over "mySphere"
{
    string radius = "five"
}

which is not considered a runtime error. In Presto (and future USD) it would be a validation error, but at scene-interrogation-time, I think we are silent about it, actually.

// cache. Attempting to collapse this expression using |= or the
// STL may result in some population being skipped and incorrect
// results.
if (_PopulateLabels(stage->GetPrimAtPath(path)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do this population in parallel?

return {};
}

std::shared_lock lock{_cachedLabelsMutex};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we need to lock here; I assume the locking is only to protect against simultaneous writes (or reads while writing is happening). But at this point, we know the cache has already been populated, so presumably it is safe to allow multiple readers to proceed in parallel?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If another thread is querying the cache, a rehash could be triggered, invalidating the iterator.

return {};
}
VtTokenArray result{std::cbegin(it->second), std::cend(it->second)};
std::sort(result.begin(), result.end());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we sort as part of population?

Ah, I guess we can't because we use sets so that we can combine inherited results. It seems like it would be nice to frontload the sorting, somehow, but there are tradeoffs to consider, and we'd want to actually measure performance to understand the impact, most likely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible it would be more efficient to use std::merge on sorted token arrays. But perhaps we could address later?


_UnorderedTokenSet uniqueElements;
{
std::shared_lock lock{_cachedLabelsMutex};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I'm not yet understanding why we need to lock once population is complete.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the previous note about the iterator lifetime.

@pixar-oss pixar-oss merged commit b266dd4 into PixarAnimationStudios:dev Oct 3, 2024
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants