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

Initial implementation of structs in MaterialX #1831

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

ld-kerley
Copy link
Contributor

This is a follow on from the previous draft PR #1684 on implementing structs.

This implementation maintains the structs in the shader code.

This is still very much a draft and needs a lot of cleanup, but posting so we can discuss if this direction is worth pursuing.

Looking for feedback on :

  • Value tokenization, currently it happens during shader generation, but perhaps there a cleaner place to put it in the document parsing, creating some sort of compoundValue subclass of the Value type?
  • What to do if a struct name clashes with a variable name in the generated shader code, this happened in my testing when I had named my new struct texcoord which is a variable name somewhere in some generated code.
  • If we need to find a more elegant way to call loadStructTypeDefs, having to add it in many places to get everything to work feels a little clunky/error prone, but perhaps this sort of refactor its beyond the scope of this work.
  • Do we need/want to support recursively nested structs? I don't believe the code as it's written will do this, but I think it would be possible.

@ld-kerley
Copy link
Contributor Author

Tagging @jstone-lucasfilm @niklasharrysson for discussion - happy to jump on a call if thats easier too.

@niklasharrysson
Copy link
Contributor

@ld-kerley Great to see this work! I like this direction a lot, where structs can remain unfolded in generated code.

Some feedback on your questions:

  • About value tokenization: I think it would be great to introduce a CompoundValue (or AggregateValue) class, as a subclass of Value, in order to do the value parsing in one central place, instead of having to deal with the valuestring parsing in many places. This would also let us validate struct values during document validation, and not only during code generation.

  • For name clashes I'm surprised this doesn't work out of the box. There is a call to registerReservedWords inside registerTypeSyntax, that should handle this. So try tracing from there and see why the struct name is not registered as a reserved word.
    https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/source/MaterialXGenShader/Syntax.cpp#L55

  • For loading in struct types from the document, perhaps we could add initialize/deinitialize methods to ShaderGenerator, to handle such things in a central place. We already have other similar methods that need to be called to initialize generation, for example the registerShaderMetadata call. So all such calls could be collected into a single init method.

  • For nested structs, it looks like the approach you have take should be able to support this. So that would be nice. That's one of the pros of not unfolding the structs, as that would have been more messy I think.

@ld-kerley
Copy link
Contributor Author

@niklasharrysson - thanks for the great feedback - I'll get working on some of those ideas.

regarding the name clashing - I don't think the struct name clashes with a reserved word, instead it clashed with a parameter name in some of the the shader code. I had named my struct texcoord, in my example, and I was getting shader compile error because of the GLSL code included from this file. the mx_image_float() takes a vec2 texcoord parameter.

I don't know if we need to obfuscate the struct names in the generated shader code somehow - add some uuid suffix perhaps?

@niklasharrysson
Copy link
Contributor

Ah I see! Yes, obfuscating the struct type names with a suffix sounds like a good idea then.

@ld-kerley
Copy link
Contributor Author

@niklasharrysson I hit a roadblock when adding a randomly generated suffix to the struct names in the generated shader code. It means the struct names are not know when the node definition author is writing the shader source, and they need to declare the type of the inputs to their functions in the shader source.

We could opt for a static suffix. texcoord -> texcoord_typedef, or something, and then node authors would be able to use the less obvious texcoord_typedef type name in their shader code.

Or, we could just not suffix at all, and instead find a way to make the error messages more informative?

The most recent push includes the AggregateValue update, and I was considering postponing the centralized initialize/deinitialize refactor for the ShaderGenerator to a separate PR, just to keep this one manageable.

@niklasharrysson
Copy link
Contributor

@ld-kerley Aha, yes that's a roadblock indeed! :)

A codegen added static suffix feels a bit awkward, since as you note, node authors need to be aware of this and append the suffix in all shader code using this data type. Perhaps it's better then to not add a suffix at all during codegen, and instead introduce a naming convention for custom data types, that authors must follow?

For example, something like:
A custom type name should have a suffix that declare the semantic of the type. Example: texcoord_struct
In addition, if the type is declared outside of stdlib, it should have a namespace prefix to minimize name collisions. Example: pxr_texcoord_struct, maya_light_struct, etc.

Would be great to have better error messages if collisions do occur. I'm not sure how though, since it's the target language compiler that finds this.

Sounds good to postpone the centralized initialize/deinitialize to another PR.

@ld-kerley
Copy link
Contributor Author

@niklasharrysson I really like the _struct suffix idea. We can add a step in the document validation to check for it. Do we want to block a render, ie. fail code gen, if we don't have the suffix? I can imagine someone could very well get lucky and not clash. Do we have other examples (I think we do because I've hit them) where a document could be invalid, but still render exactly as intended?

Also you suggest using the semantic of the type, does that mean that this example from the current spec
<typedef name="spectrum" semantic="color"/>
should be re-written to
<typedef name="spectrum_color" semantic="color"/>
?

Or perhaps this suggestion infers a completely new XMLTag/MaterialX type? <structdef> ? and we would leave <typedef> alone as its written in the new spec (with no <member> child tag). That way the new concept can more cleanly have its own set of rules?

<structdef name="texcoord_struct">
  <member name="ss" type="float" value="0.5"/>
  <member name="tt" type="float" value="0.5"/>
</structdef>

Given this feature never worked before, it doesn't seem too difficult, from a backwards compatibility perspective, to pivot this way? Perhaps @dbsmythe or @jstone-lucasfilm have input here?

@niklasharrysson
Copy link
Contributor

@ld-kerley About validation of a naming convention, I'm not sure we need to do that, since even if we do there is no guarantee that this will help to resolve name conflicts. At least in theory, a node author could use the same name for a variable in the code added for a node implementation, even if both a prefix and suffix is used.

So perhaps the best we can do is to provide guidelines for how to name you data types and identifiers. And if there is a conflict anyway, resulting in shader compile errors, it's up to the node author to resolve it.

With this in mind perhaps it would be best to just stick with the typedef element, even for structs. It feels more consistent since after all it's a type you're defining.

As for the naming convention to use, the one I gave was just an example, and it would be great to get others feedback on this as well.

@jstone-lucasfilm
Copy link
Member

@ld-kerley We've now merged development work on MaterialX 1.39 back to the main branch of MaterialX, in preparation for the final weeks of development on the 1.39.0 release. When you have a chance, could you retarget this pull request back to the main branch as well?

@ld-kerley ld-kerley changed the base branch from dev_1.39 to main May 31, 2024 01:16
@ld-kerley ld-kerley changed the base branch from main to dev_1.39 May 31, 2024 01:16
@ld-kerley ld-kerley force-pushed the struct_experiment_dont_unfold branch from 4403f1e to b04f9ca Compare May 31, 2024 01:19
@ld-kerley ld-kerley changed the base branch from dev_1.39 to main May 31, 2024 01:19
@ld-kerley
Copy link
Contributor Author

@jstone-lucasfilm - done - and also pushed my latest.

@niklasharrysson - this includes the _struct suffix, to the example only - no validation. And also now should allow for structs-of-structs - including in the GLSL/MSL rendering path.

Logged an issue (#1850) for the common init/de-init for ShaderGeneration.

@ld-kerley ld-kerley force-pushed the struct_experiment_dont_unfold branch from 9ca2b71 to 9ae8173 Compare June 2, 2024 23:27
Copy link
Contributor

@niklasharrysson niklasharrysson left a comment

Choose a reason for hiding this comment

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

This is coming along nicely! I added some suggestions above.

Apart from those there seems to be some build errors caught by CI, which mainly looks like compile warnings that should be fixed.

source/MaterialXGenShader/TypeDesc.h Outdated Show resolved Hide resolved
source/MaterialXCore/Definition.h Outdated Show resolved Hide resolved
libraries/experimental/experimental_defs.mtlx Outdated Show resolved Hide resolved
source/MaterialXCore/Util.cpp Show resolved Hide resolved
source/MaterialXCore/Util.cpp Outdated Show resolved Hide resolved
source/MaterialXCore/Value.cpp Outdated Show resolved Hide resolved
source/MaterialXGenShader/Syntax.cpp Outdated Show resolved Hide resolved
source/MaterialXGenShader/TypeDesc.cpp Outdated Show resolved Hide resolved
source/MaterialXGenShader/TypeDesc.cpp Outdated Show resolved Hide resolved
Add "_struct" suffix to struct type name to avoid potential name clashes with variable names.
Add AggregateValue to centralize struct string parsing. Add support for recursive structs in places - may not be complete yet - centralized syntax parsing - and unit tests pass currently.
refactor struct experiment to not unfold the structs, but instead maintain them in the shaders
@ld-kerley ld-kerley force-pushed the struct_experiment_dont_unfold branch from 9ae8173 to 42b3e6d Compare June 11, 2024 22:37
@ld-kerley
Copy link
Contributor Author

I think all the tests are passing now and I addressed the @niklasharrysson notes above - moving the the example scenes to the test suite required a little re-working of the testing framework to add another libraries location to the search path.

Copy link
Contributor

@niklasharrysson niklasharrysson left a comment

Choose a reason for hiding this comment

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

These changes looks great to me. I only added a few extra comments above.

Thanks @ld-kerley!

@ld-kerley ld-kerley changed the title Draft : Implementing structs part 2 - no struct unfolding Implementing structs - no struct unfolding Jun 13, 2024
jstone-lucasfilm pushed a commit that referenced this pull request Jul 3, 2024
This is a first pass at updating the specification to reflect the upcoming struct support in PR #1831.  A lot of this is lifted from the old 1.38 specification pdf, as that was largely the inspiration for the implementation.
@jstone-lucasfilm jstone-lucasfilm changed the title Implementing structs - no struct unfolding Initial implementation of structs in MaterialX Jul 13, 2024
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.

None yet

3 participants