meshopt_Allocator can be used to allocate typed buffers and automatically
deallocate them later. Unlike meshopt_Buffer, the result is a pointer
that can be dereferenced at no additional cost in Debug.
This change migrates from meshopt_Buffer to meshopt_Allocator to
improve performance in Debug. Note that we forgo bounds checking in
Debug by using this type, but we were already inconsistent about using
pointers vs buffers - address sanitizer will help ensure memory safety
and we won't need to compromise Debug performance in regular builds for
that.
The following times are taken in MSVC 2017 x64 Debug using buddha.obj:
meshopt_analyzeOverdraw: 10.1s -> 7.7s
meshopt_analyzeVertexCache: 5.4s -> 0.9s
meshopt_analyzeVertexFetch: 1.2s -> 0.7s
meshopt_optimizeOverdraw: 1.0s -> 0.7s
meshopt_optimizeVertexCache: 26.1s -> 5.6s
meshopt_simplify: 3.7s -> 2.8s
We had a mixture of files that were using LF and files that were using
CRLF. For consistency convert *everything* to LF.
This unfortunately means that for most code blame will become less
useful since you'd need to go past this commit but such is life.
In cases where the vertex buffer is shared between several meshes, such
as simplification, the ATVR metric wasn't taking this into account and
reported unrealistically low values.
Fix this by computing unique vertex count - all vertices that ever
entered the cache contribute to this - and using it instead.
While details of the hardware behavior are not known and vary between
vendors and GPU generations, based on the results of vcachetester it
seems that both AMD and NVidia follow a similar algorithm:
- Triangles are buffered up to a certain number, after which the cache
is flushed
- Within the buffer, a FIFO cache is used to gather vertices to fill a
warp/wavefront
- When a warp is full, it's dispatched for execution; cache is flushed
at this point - new warps don't share data with old warps
There are some pecularities, for example in case of NVidia the 9xx
series has a cache that has 16 vertices but the cache behavior is
somewhat odd - even if a vertex is still in FIFO cache it will not be
reused in some conditions - whereas the 10xx series seems to be able to
reuse any vertex from the current warp, making the effective cache size
32.
AMD seems to consistently follow the outlined procedure with cache size
14, wavefront size 64 and triangle buffer size 128 for multiple GPUs
from GCN era; pre-GCN cards (e.g. AMD Radeon HD 6xxx) seem to follow
a different - possibly more traditional? - algorithm.
Intel more or less has a simple 128-entry FIFO with no obvious warp
boundaries; there is some sort of limitation on reuse but I can't yet
figure out what the pattern is, so for now we'll assume Intel has a
vanilla 128 entry FIFO cache.
This change also removes LRU simulation because GPUs don't seem to
actually use LRU, so it's not very useful.
This mostly makes profiler output nicer but also works around build
non-determinism for some compilers (that generate unique symbol names
for anonymous namespaces in each compilation attempt).
Now all main entrypoints to the library are C-style - they use
non-mangled symbols without any default arguments or other C++ features.
We still have templated wrappers in namespace meshopt.
The implementation currently uses anonymous namespaces to reduce the
amount of differences, which may need some cleanup.
Instead of doing many redundant data movements in std::vector, do a
single-pass copy with a branchless loop body to a new cache, similar to
what we do in the optimization algorithm.
This makes LRU analyzer ~30% faster.
Since hardware cache resembles LRU more than it does FIFO, LRU analysis
is useful for developing the linear cache optimization.
To match behavior of optimizeVertexCache, we switch to LRU analysis mode
for cache_size=0.
We now refer to pre-transform cache optimization as "vertex fetch
optimization".
We now refer to post-transform cache optimization as "vertex cache
optimization".
This seems to be a bit more modern and easier to explain.
ACMR is a good metric in general but the optimal ACMR number for a given
mesh depends on the triangle/vertex ratio - ACMR of 0.5 is impossible to
reach for certain meshes. In contrast, if we assume all vertices are
required by a given mesh, ATVR (average transform to vertex ratio) has
to be at least 1.0 (each vertex is transformed once), and represents the
amount of over-transformation due to vertex cache misses.