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
Compact edges #5581
Compact edges #5581
Conversation
@wgoehrig @paulius-valiunas this PR includes a tile format version bump and a change to the default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through all of the code and it all looked good to me.
@grigasp presentation tests failing.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detailed commit comment much appreciated!
Looks like it fails because your branch is behind |
Thanks. Bit of a pain to have to start from scratch with the native builds every time there's an update to itwinjs-core. |
I checked the pipeline, i believe its running fine. These are the branches its picking up |
imodel-native: https://github.com/iTwin/imodel-native/tree/pmc/compact-edges
PR
imodel-native-internal: https://github.com/iTwin/imodel-native-internal/tree/pmc/compact-edges
PR
The amount of geometry required to render the edges of a surface is many times larger than that required for the surface itself. So, when edge display is turned off in a viewport (the common case for 3d views), the frontend requests tiles that omit the edges, to avoid having to download much more data than it actually needs. When edge display is enabled, it requests an entirely separate set of tiles that do contain the edges.
The introduction of indexed edges significantly reduced the amount of memory reserved (in tiles and on the GPU) for edges. We had hoped it would reduce it sufficiently that we could produce only one set of tiles and have the client discard the edges if it did not wish to display them. The reduction was not sufficient to justify this.
With the introduction of support for decoding tile contents in a web worker we now have the opportunity to offload a little more computation to the client without impacting the user experience. So this PR (and the linked native ones) updates the iMdl format to support encoding just enough information about the edges of the surface, in as compact a form as possible, to enable the client to construct the edge geometry itself during tile decoding. We expect the additional reduction in tile size to finally enable us to stop producing tiles containing no edge data.
A surface is defined as a triangle mesh. Its index buffer describes the triangles as triplets of consecutive indices into the vertex table. In the example below, there are 4 vertices and 2 triangles described by the index list
[0,1,2, 0,3,2]
.The visibility of the edges of each of these triangles can be categorized as one of: never visible, always visible, or visible based on viewing angle (i.e., a silhouette). We can lay out another array parallel to the index array specifying the visibility of each edge. Assuming the diagonal above is a silhouette, the vertical edges are always visible, and the horizontal ones are never visible, we have
[visible,hidden,silhouette, hidden,visible,silhouette]
. Because we can convey the visibility of each edge using only 2 bits, we encode the edge visibility buffer as a bitfield. So for each 24-bit vertex index in the mesh, we add 2 more bits for the edge visibility.However, in the example above the shared (silhouette) edge between vertices 0 and 2 appears twice - once per triangle. We don't want to draw nor allocate memory for the same edge twice, so when encoding we ensure shared edges appear only once:
[visible,hidden,silhouette, hidden,visible,hidden]
.To compute the visibility of silhouettes, we need a vector normal to each adjacent face. We can infer the third vertex of one of the faces, but not the other. So, if we wanted to compute the normals on the frontend, we'd need to store the 24-bit index of the third vertex of the opposite face. Alternatively, we can compute both normals on the backend and encode them as a pair of 16-bit oct-encoded normals. Trading one extra byte to save a ton of extra client-side computation seemed preferable so that's what we do. The pairs of normals are encoded into a separate buffer such that the Nth pair of normals corresponds to the Nth silhouette encountered while traversing the visibility buffer.
On the client-side all of this data is decoded into the same
IndexedEdgeParams
that we would previously have encoded directly into the tile. So, the amount of GPU memory is unchanged, the amount of computation on the frontend is slightly increased (but offloaded from the UI thread), and the size of the tiles is significantly reduced.Ultimately we intend to enhance the mesh export service to encode edge visibility this way so that only one tileset will need to be produced regardless of whether the client has enabled display of edges in the viewport.
The table below shows the sizes in bytes of the encoded tiles without edges and with compact edges. The last 3 rows summarize the results.
When comparing tile sizes for tiles without edges vs those with non-compact indexed edges, the max % increase is 179%, min is 22%, and the mean is 73% So compact edges produce an order of magnitude reduction in tile size.
TODO