OpenSubdiv/opensubdiv/vtr/OVERVIEW.txt
manuelk c399655dcc Landing 3.0.0.alpha
Sync'ing the 'dev' branch with the 'feature_3.0dev' branch at commit 68c6d11fc36761ae1a5e6cdc3457be16f2e9704a

The branch 'feature_3.0dev' is now locked and preserved for historical purposes.
2014-09-05 15:07:46 -07:00

256 lines
16 KiB
Plaintext

This text file is written to accompany the opensubdiv/vtr source during review. It is intended to
give a high level overview of the organization of the source, the rationale for various decisions,
as well as highlight open issues or situations where the choices made thus far warrant further
deliberation.
Vtr is the newly proposed layer to support the "vectorized topological representation" of a mesh
and its associated refinement. Vtr is intended for internal use and is currently accessed through
the public FarTopologyRefiner class and its Factory class that internally transforms an instance of
a client's mesh into Vtr form. FarTopologyRefiner is intended to be one of a set of modular, table-
oriented classes made available for public use in the Far layer. As such, its debatable whether
the Vtr classes that support FarTopologyRefiner should be part of Far or the new Vtr layer. So we
are prepared to rename all Vtr classes as Far in future if necessary.
Currently there are three header files containing the following:
1) Common Vtr utilities
2) Level - a class representing complete topology for a level
3) Refinement - a class mapping a parent to a child level
which will be reviewed below. A synopsis of each provides some of the motivation behind the
contents along with current issues being addressed. More details may be provided in the header
itself.
Vtr is very much a work in progress. There is still considerable functionality to be added and
potentially more performance tuning to be done. Based on current and significant performance
improvements of the refinement process over Hbr though, completing the desired functionality is
currently the priority. Work to be done includes:
- support for sparse (feature-adaptive) refinement
- addition of more per-component tags to propogate with refinement (e.g. holes, etc.)
- support for tri-split refinement required by Loop subdivision
- specializations for regular levels and refinements
Since intended for internal use, we can continue to take liberties with the interfaces for the near
term, e.g. allowing direct member access as needed ala FarSubdivisionTables. The FarTopologyRefiner
class and its Factory class will be the public interface to the functionality provided here, and so
they warrant far more attention (and will be discussed as part of proposed Far changes/additions).
1) Common Vtr utilities:
Header:
vtr/types.h
Synopsis:
The style of the existing OpenSubdiv code is to nest the definitions of utility types and
classes in other classes. There are several in Vtr that are used equally by multiple classes, and
its debatable if they belong in one or another. So currently <vtr/types.h> includes a number of
such declarations for public use within Vtr (or Far). These include:
- declarations of types used for component indices, sharpness, etc.
- constant and inline method for testing index validity
- declarations of commonly used array types
These could arguably be moved into the next class discussed (i.e. Level), but as with the choice
of whether functionality belongs in Vtr or Far or not, but bigger issue at hand is the nature of
the functionality itself.
With data in Vtr, Far and elsewhere stored as collections of arrays aggregated into much larger
arrays, access to these sets of arrays within a larger context is frequent, and so making it both
convenient and efficient is of value. A generic "array interface" class was made available to
support this (and, if ultimately warranted, its debatable whether it should be in Sdc or Vtr).
This class is nothing more than a pair that defines the extent of an array along with a subset of
the interface of std::vector for accessing its entries -- it does not own the data that it refers
to but simply allows access to its elements. Arguably we could pass pairs of values around --
either begin/end or begin/size -- but the frequency of such occurrences is considerable and it
doubles the number of function parameters in many cases (particularly those requiring more than
one array) so declaring a simple class for this purpose makes a big difference to code readability.
The term "Vector" is used for types defined in terms of std::vector and "Array" (or "Accessor" and
"Modifier" for const/non-const interfaces -- a personal style I adopted out of habit and am happy
to drop) for types defined in terms of these array interfaces. The two main Vtr classes described
below make heavy use of these.
Questions:
- what definitions really need to be separate from other Vtr classes?
- use of "int" versus "unsigned" int for Index (unsigned allows twice the size)?
- usefulness and location of "array interface" types?
2) Level:
Header:
vtr/level.h
Synopsis:
Level is a complete topological description of a subdivision level, with the topological
relations, sharpness values and component tags all stored in vectors (literally std::vectors,
but easily changed via typedefs). There are no classes or objects for the mesh component types
(i.e. faces, edges and vertices) but simply an integer index to identify each. It can be viewed
as a structure-of-arrays representation of the topology -- any property related to a particular
component is stored in an array and accessible using the index identifying that component. So
with no classes the for the components, its difficult to say what constitutes a "vertex" or a
"face" -- they are each the sum of all the fields scattered amongst the many vectors included.
Level represents a single level of a potential hierarchy and is capable of representing the
complete base mesh. There are no members that relate data in one level to any other, either below
or above. As such, any Level can be used as the base level for a new subdivision hierarchy
(potentially more than one). All relationships between separate levels are maintained in the
Refinement class that follows.
Level requires the definition of and associations between a fixed set of indexable components
for all three component types, i.e. an explicit edge list in addition to the expected set of
vertices and faces. There are no explicit component objects in the representation -- only an
integer index (Index) identifying each component within the set and data associated with that
component in the various vectors. The topology is stored as six sets of incident relations
between the components -- two each for the two other component types incident each component
type, i.e.:
- for each face, its incident vertices and incident edges
- for each edge, its incident vertices and incident faces
- for each vertex, its incident edges and incident faces
I am loose with my terminology here and use "incident" when "adjacent" may be more technically
correct. The collection of incidence relations is a vectorized variation of AIF (the "Adjacency
and Incidence Framework"). The set of these six incidence relations is not minimal (only four
are required, but that set excludes the most desired face-vertex relation) but all six are kept
and maintained to facilitate faster refinement. While the sizes of several vectors are directly
proportional to the number of vertices, edges or faces to which the data is associated, the
sizes of some of the vectors for these relations is more cumulative and so additional vectors of
offsets is required (typical of the face-vertex list commonly used as the minimal definition of
mesh topology). The sizes and offsets of each set of incident components in these larger arrays
is a pattern we also see in the FarSubdivisionTables.
Vectors for the sharpness values associated with crease edges and corner vertices are included
(and so sized according to the number of edges and vertices), along with additional tags for the
components that may be helpful to refinement (i.e. the type of subdivision Rule associated with
each vertex).
In general, this is not that much different from what the FarSubdivisionTables includes. It does
differ from FarSubdivisionTables in that:
- it includes topology and associated data only for a single level
- the face-edge relation (edges incident each face) is included to support refinement
- incidence relations are ordered based on the component in a level, not child vertex
- there is no sorting or other permutation based on any computation kernels required
It was mentioned that there is no data relating a level to any other, but that is not yet entirely
true. The only suggestion that an instance of Level is part of any greater collection is an
integer "_depth" member. This was included to indicate when the topology of a level might be
regular or not wrt some subdivision scheme (knowing that all faces in a level are quads or tris
can allow us to specialize and reduce memory). While useful, there are other ways to indicate
this and so "_depth" is likely to be replaced with other members that serve the same purpose.
A Level is really just a container for data in a subdivision level, and so its public methods
are primarily to access that data. Modification of the data is protected and only made available
to classes that are intended to construct Levels -- currently the Far factor class that is
responsible for building the base level, and the Refinement class that constructs subsequent
levels during refinement.
One of the advantages in storing data in what is essentially a structure-of-arrays, rather than
the array-of-structures more typical of topological representations, is that we can be more
selective about memory usage in some cases. Particularly in the case of uniform refinement, when
the data in subsequent levels is typically 4x its predecessor, we can minimize what we either
generate or keep around at each level. For instance, if only a face-list is required at the
finest level, we only need to generate one of the six topological relations -- the vertices
incident each face. So when we do keep Levels around in memory (as is the case with the
FarTopologyRefiner) we do have do have the opportunity to prune what is not strictly necessary
after the refinement. Just as with construction, whatever classes are privileged to construct
a Level are likely those that will be privileged to prune its contents when needed.
Since Level is purely topological, there is no template parameter required as with the case
of HbrMesh<T>. With no template instantiation required, the source is defined in a .cpp file
corresponding to the header, rather than in the header itself. Other than the utility types
defined in a separate header, this is true of all Vtr source.
Upcoming work:
- the addition of more tags (packed into bitfields) for each component to help support
sparse refinement and any needs that may arise
- identifying opportunities to prune unused members after refinement
Questions:
- is it worth aggregating the many vector members into a smaller set at this point (and
so using the array interface for the many members sharing the same vector)?
3) Refinement:
Associated headers:
vtr/refinement.h:
Synopsis:
While Level contains the topology for a subdivision level, Refinement is responsible
for creating a new level via refinement of an existing one, and for maintaining the relationships
between the components in the parent and child levels. So a simplified view of a subdivision
hierarchy with Vtr is a set of Levels with a Refinement between each successive pair.
Refinement is a friend of Level and will populate a child level from a parent given a set of
refinement parameters (eventually). Currently only uniform refinement is implemented, but work to
support more general "selective" refinement is underway. Feature-adaptive refinement is just one
form of selective refinement -- the criteria being the topological features of interest (creases
and extra-ordinary vertices). The intent is to provide greater generality to facilitate the
refinement of particular regions of interest or more dynamic/adaptive needs.
The topology of the refinement, or rather the topology of both the refinement and the Level
level that it populates, is determined by a trait of the desired subdivision scheme applied --
currently only quad-splitting is supported for Catmark and Bilinear, but tri-splitting will
eventually need to be added for Loop. So there is no specializing the refinement in terms of
subdivision scheme, only in terms of the topological splitting associated with it. There are
opportunities to reduce both the storage of topology in Level and the expense of refinement
in VtrRefinment for cases when the mesh has become regular wrt the subdivided topology (i.e. all
quads or all tris) but these are not yet being explored. Subclassing Refinement for the two
current topological splitting types is currently under consideration, but more immediate needs
for feature-adaptive support for Catmark will likely defer any efforts in support of Loop.
While Refinement populates a new child Level as part of its refinement operation, it also
accumulates the relationships between the parent and child level (and as with Level, this data
is stored in vectors indexable by the components). Currently the associations between components
in the two levels is uni-directional -- child components are associated with incident components
of a parent component based on the parent components topology, so we have a parent-to-child
mapping (one to many). We tried to avoid having a complementary child-to-parent mapping to
reduce memory (particularly in the case of uniform refinement) as it often was not necessary,
but there is a growing need for it in some cases once the hierarchy is constructed, so it is
likely it will be added.
One of the advantages of the structure-of-arrays representation in both Level and Refinement
is that we can make more dynamic choices about what type of data we choose to allocate and use
based on needs. For instance, we can choose between maintaining the parent-child or child-parent
mapping in Refinement, or both if needed, and we can remove one if no longer necessary. An
active example of this is uniform refinement -- if we only require the face-vertex list at the
finest subdivision level, there is no need to generate a complete topological description of that
level (as would be required of more traditional representations), and given that level is 4x the
magnitude of its parent, the savings are considerable.
Currently there is nothing specific to a subdivision scheme in the refinement other than the
type of topological splitting to apply. The refinement does subdivide sharpness values for
creasing, but that too is independent of scheme. That may not last forever though -- at least
two areas where specialization according to scheme may be necessary are:
- computation (but not application) of stencil weights for refined vertices
- identification of extra-ordinary feature as part of feature-adaptive analysis
In both cases though, these specializations may end up external to Vtr. Stencil weights may only
be needed once for application to a single set of data, and that may be managed by a separate
class in Far. (Currently I optionally compute and store them in Refinement as part of an
experiment for Manuel, but they are not intended to persist, and so are hard-coded to be for
Catmark only for the duration of the experiment.) If stencil weights are to be computed and
stored -- as with FarSubdivisionTables -- that may also be done where needed. Similarly, the
feature-adaptive analysis is also likely to be part of the FarTopologyRefiner, where the refinement
parameters are publicly specified and internally translated into Vtr operations to be applied.
Given that Vtr and Far might eventually be merged, whether these specializations for scheme occur
in Vtr or Far is less important -- the point here is that Refinement should not require them.
Like Level, Refinement is also purely topological, free of any template parameter for a
vertex type, and so its source is defined in a .cpp file corresponding to its header.
Upcoming work:
- support sparse refinement (marking/selecting components, required incident components, etc.)
- provide member vectors for the inverse mapping of components (i.e. child-to-parent)
- redefine component traversal from parent to child where necessary
- support tri-split topology in addition to current quad-split for Catmark and Bilinear
Questions:
- ?