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

DYN-2538 Generate single RenderPackage per Node #11395

Merged

Conversation

saintentropy
Copy link
Contributor

@saintentropy saintentropy commented Jan 11, 2021

Purpose

Note: I was cleaning up my Dynamo git stash over the holiday break and this was too good to leave on the shelf. This builds on some work that @LongNguyenP, @Dewb, @aparajit-pratap, and I looked at a while back. I picked up and fixed the regressions we noted previously.

The purpose of this PR is a performance and memory optimization for the render pipeline. The main architectural change is modifying the assumption that we generate a single RenderPackage object per every individual return object for a node. Instead we aggregate all node tessellation data into a single RenderPackage recursively. This improves tessellation performance as the HelixRenderPackage is a large .NET object with a non-trivial init time in addition to a large saving in memory; specifically one render package with all the tessellation data is much smaller then many render packages with partial tessellation data. This approach also creates performance benefit and memory savings when the tessellated data is added to the Helix scene. Processing one RenderPackage per AggregateRenderPackages call is dramatically more efficient than multiple render packages as there are numerous collection interactions and temporary allocation. In code, this change was enabled by generating the RenderPackage first in GetRenderPackagesFromMirrorData before the recursive loop to fill the render package data. Previously it was generated within the loop.

There are a few things that require changes to support features which assumed a single item = a single RenderPackage.

  • Labels (ie the labels that appear in the scene context by selecting items in the node preview). The existing implementation encoded label information in the Description property of the RenderPackage. That encoding was not well documented making it nearly impossible to add labels to the your own tessellation method overrides. Here I introduce a new interface, IRenderLabels, which provides methods on the render package to add label data and placement. The new underlying storage implementation matches the existing LabelPlaces data structure in the HelixWatch3DViewModel so that adding labels requires much less string parsing and processing.

  • Texture maps. The original texture map implementation in the Colors and ColorStride property assumed only one texture map per the mesh geometry in the RenderPackage. To support aggregation of multiple mesh geometries within a single RenderPackage with unique textures, I introduce a new interface, IRenderPackageSupplement, which supports adding lists of Colors and ColorStrides along with their associated mesh vertex ranges. The Colors and ColorStride property as well as the SetColors method are marked as obsolete with notes to use the new methods in the IRenderPackageSupplement.

  • Color adding methods on IRenderPackage. While methods to add vertex color like AddPointVertexColor still work fine with this change, when utilized in tessellation calls, methods like ApplyPointVertexColors overwrite the previous data vs appending. That can cause data loss as they are currently used. I introduce new methods to IRenderPackageSupplement like InsertPointVertexColorRange and AppendPointVertexColorRange to allow additions without overwriting existing data. The old methods are also marked as obsolete with notes to use the new methods in the IRenderPackageSupplement. The GeometryColor nodes are updated to use the new methods.

These new features are being introduced with a minor release and not Dynamo 3.0. While we can update the tessellation implementation in Dynamo and Dynamo integrations, Third Party tessellation functions also need to stay working. For each of the Obsolete methods above I introduced an Exception to allow the GetRenderPackagesFromMirrorData implementation to detect their usage. Specifically within IRenderPackageSupplement there is a boolean AllowLegacyColorOperations that can be set to false which will cause the LegacyRenderPackageMethodException to be thrown when the obsolete methods are called. In that case, we roll back any changes to the RenderPackage that occurred within the Tesselate call and generate a new RenderPackage with AllowLegacyColorOperations set to true and re-Tesselate. This individual render package is added to the RenderPackageCache and the outer loop resumes. In the transition to Dynamo 3.0, the obsolete methods can be removed as well as this fallback.

Todo

  • Add tests for the new functionality
  • Update Tessellate implementation in DynamoRevit, other integrations, samples, or internal packages. This is not strictly required due to the fallback mechanism.
  • Fix the Label.ByPointAndString node to work with the new label mechanism so that the node actually work.
  • ITransformable needs to be refactored similarly to the Texture Map implementation. This may be covered under a separate PR as we can possibly take advantage of the refactor to introduce instancing mechanism.

Examples

PointsNew.mp4
PointsOld.mp4
LinesNew.mp4
LinesOld.mp4

For most point and curve tessellation the speed and memory improvement will be linear based on the number of tessellated items. However for meshes the speed and memory improvement is dramatic. The original implementation would take exponentially longer to add mesh geometry to the scene depending on the number of items. This refactor restores performance to a linear relationship

# Meshes Now (s) Memory Alloc Before (s) Memory Alloc
~10000 7.95s 334mb 17.12s 668mb
~30000 17s 1.1gb 86s 2.1gb
~68000 38s 2.6gb Crash NA

Declarations

Check these if you believe they are true

  • The codebase is in a better state after this PR
  • Is documented according to the standards
  • The level of testing this PR includes is appropriate
  • User facing strings, if any, are extracted into *.resx files
  • All tests pass using the self-service CI.
  • Snapshot of UI changes, if any.
  • Changes to the API follow Semantic Versioning and are documented in the API Changes document.
  • This PR modifies some build requirements and the readme is updated

Reviewers

@mjkkirschner @aparajit-pratap @QilongTang

… render package per node vs per node return obj
…usage of the color methods in IRenderPackage
…or methods in tessellation calls. If Detected, revert changes to render package and generate a new render package with legacy methods per IGraphicItem.
@aparajit-pratap aparajit-pratap marked this pull request as draft January 12, 2021 21:08
@mjkkirschner
Copy link
Member

@aparajit-pratap one downside of marking this PR as a draft is that the tests do not run - we'd have to modify the code that runs on the self serve pull job - and I think it makes use of https://github.com/microsoft/PowerShellForGitHub ... I gave up once I got that far.

@aparajit-pratap aparajit-pratap marked this pull request as ready for review January 13, 2021 03:33
@aparajit-pratap aparajit-pratap added the DNM Do not merge. For PRs. label Jan 13, 2021
@mjkkirschner
Copy link
Member

mjkkirschner commented Jan 13, 2021

@saintentropy it would be good to run the wpf visualization test jobs - there are versions of that job that run with warp and another that runs on a real GPU - these do image comparisons so should be useful.

@saintentropy saintentropy removed the WIP label Jan 15, 2021
@saintentropy saintentropy changed the title WIP DYN-2538 Generate single RenderPackage per Node DYN-2538 Generate single RenderPackage per Node Jan 15, 2021
}
}

private void GetTessellationDataFromGraphicItem(Guid outputPortId, IGraphicItem graphicItem, IRenderPackage package, string labelKey)
Copy link
Contributor

@aparajit-pratap aparajit-pratap Apr 23, 2021

Choose a reason for hiding this comment

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

I think package should be passed as a ref parameter as it can be assigned a new object inside the function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely! Good catch

@aparajit-pratap
Copy link
Contributor

@saintentropy thanks for addressing the review comments and adding inline documentation. Also thanks for clearer names for the new APIs. I have just two remaining questions, then LGTM.

@saintentropy
Copy link
Contributor Author

Hi @aparajit-pratap Added some of fixes from the comments and a lot more tests

@saintentropy
Copy link
Contributor Author

I also added the single item label fix. The regression was introduced in this PR -> #10989. That PR also added the non-functional DisplayLabel node back into core so this PR fixes both the node and the label regression.

@aparajit-pratap
Copy link
Contributor

@saintentropy when I saw your visualization tests I remembered you could also add image comparison tests that @mjkkirschner introduced here https://github.com/DynamoDS/Dynamo/blob/master/src/VisualizationTests/HelixImageComparisonTests.cs. This could be especially useful for the texture map graph you showed with the 3 surfaces. It doesn't have to be in this PR, can be a separate one as we discussed.

Comment on lines +192 to +199
try
{
p.AppendMeshVertexColorRange(testColors);
}
catch(Exception e)
{
Assert.AreEqual(e.GetType(), typeof(Exception));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Assert.ThrowsException<Exception>(() => p.AppendMeshVertexColorRange(testColors));

Copy link
Contributor

Choose a reason for hiding this comment

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

How are these exceptions dealt with in Dynamo in a real situation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All the exceptions (my new ones and the old ones) within the RenderPacakge implementation are caught here -> https://github.com/DynamoDS/Dynamo/blob/master/src/DynamoCore/Scheduler/UpdateRenderPackageAsyncTask.cs#L303. The net affect is that the node does not display geometry in the background preview.

Copy link
Contributor Author

@saintentropy saintentropy Apr 27, 2021

Choose a reason for hiding this comment

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

The only difference in the new implementation is the specific handling of the LegacyRenderPackageMethodException

{
p.AppendLineVertexColorRange(testColors);
}
catch (Exception e)
Copy link
Contributor

@aparajit-pratap aparajit-pratap Apr 26, 2021

Choose a reason for hiding this comment

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

Can we make these exceptions thrown be more specific? In the implementation I mean.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I can cover these in the next PR as I add more test coverage.

@saintentropy saintentropy removed the DNM Do not merge. For PRs. label Apr 27, 2021
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.

3 participants