Pretty straightforward this time, we already have a single struct
pointer containing all needed data (or nearly).
And we gain about 10-15% speed on tracking! :)
Two more 'not really useful' cases (OMP only shows some noticeable
speedup with above 1M elements, and since this is quick operation anyway
compared to even ather basic operators, gain is in the 1% area of total
processing time in best case).
So not worth parallelizing here, we'll gain much more on tackling heavy
operations. ;)
And BMesh is free from OMP now!
Performances tests on this one are quite surprising actually...
Parallelized loop itself is at least 10 times quicker with new BLI_task
code than it was with OMP. And subdividing e.g. a heavy mesh with 3
levels of multires (whole process) takes 8 seconds with new code, while
10 seconds with OMP one. And cherry on top, BLI_task code only uses
about 50% of CPU load, while OMP one was at nearly 100%!
In fact, I suspect OMP code was not properly declaring outside vars,
generating a lot of uneeded locks.
Also, raised the minimum level of subdiv to enable parallelization,
tests here showed that we only start to get significant gain with subdiv
levels of 4, below single threaded one is quicker.
Those three ones were actually giving no significant benefits, in fact
even slowing things down in one case compared to no parallelization at
all (in `BM_mesh_elem_table_ensure()`).
Point being, once more, parallelizing *very* small tasks (like index or
flag setting, etc.) is nearly never worth it.
Also note that we could not easlily use per-item parallel looping in
those three cases, since they are heavily relying on valid
loop-generated index (or are doing non-threadable things like allocation
from a mempool)...
Previously outcome depended on order of edges,
now the longest boundary edges are rotated first,
then the faces connected edges.
This gives more predictable results, allowing regions containing
a vertex fan to be rotated onto the next vertex.
That was a nasty one, Debug build would never have any issue (even tried
with 64 threads!), but Release build would deadlock nearly immediately,
even with only 2 threads!
What happened here (I think) is that gcc optimizer would generate a
specific path endlessly looping when initial value of virtual_lock was
FLT_MAX, by-passing re-assignment from v_no[0] and the atomic cas
completely. Which would have been correct, should v_no[0] not have been
shared (and modified) by multiple threads. ;)
Idea of that (broken) for loop was to avoid completely calling the
atomic cas as long as v_no[0] was locked by some other thread, but...
Guess the avoided/missing memory barrier was the root of the issue here.
Lesson of the evening: Remember kids, do not trust your compiler to
understand all possible threading-related side effects, and be explicit
rather than elegant when using atomic ops!
Side-effect lesson: do check both release and debug builds when messing
with said atomic ops...
Using atomic cas correctly is really hairy... ;)
In this case, the returned value from cas needs to validate *two*
conditions, it must not be FLT_MAX (which is our 'locked' value and
would mean another thread has already locked it), but it also must be
equal to previously stored value...
This means we need two steps per loop here, hence using a 'for' loop
instead of a 'while' one now.
Note that collisions are (as expected) very rare, less than 1 for 10k
typically, so did not catch the issue initially (also because I was
mostly working with release build to check on performances...).
`BM_mesh_normals_update` was converted from OMP to new parallel iterator code,
basic test with heavily subdivided cube (24.5k faces) gives:
- old OMP code: average 10ms per run.
- new BLI_task code: average 6ms per run.
So new code seems to be easily 40% quicker, in addition to getting rid of OMP. ;)
Reviewers: sergey, campbellbarton
Differential Revision: https://developer.blender.org/D2930
It merely uses the new thread-safe iterators system of mempool, quite
straight forward.
Note that to avoid possible confusion with two void pointers as
parameters of the callback, a dummy opaque struct pointer is used
instead for the second parameter (pointer generated by iteration over
mempool), callback functions must explicitely convert it to expected
real type.
Also added a basic gtest for this new feature.
This will allow threaded tasks to 'consume' all mempool items in
parallel tasks, each one working on a whole chunk at once (to reduce
concurrency managing overhead).
Adapted from http://www.pixelbeat.org/programming/gcc/static_assert.html.
Note that this macro just discards error message, so error when building
is much less nice than with gcc's _Static_assert... But error log will
point to right place in code, so should still be OK.
Pixel size was not initial early enough. For first time this was not a problem
because the bevel amount starts at 0 then, and after the mouse moves the pixel
size is initialized. For the second time the bevel amount starts at a non-zero
value, and it failed then.
These and other non-RGB passes should always be stored as full float, the
precision loss is too unpredictable.
Related to T53381, but that one is about file output nodes where we don't
know the type of data being saved currently.
This reverts commit d749320e3b.
It's possible the container struct is larger,
we could do sizeof checks that falls back to memmove
but rather avoid complicating things.
Just removing it, such cases are not bottlenecks and not worth the
complication of doing real threading with own BLI_task.
Other (remaining) usages may be relevant, need case-by-case check.
This was expected behavior for over-exposured lamps when the mode was originally
created for Tears of Steel. Turns out, there could be really bad green screen in
real production which will only have green (or rather screen) channel over
exposured.
Tweaked condition now so we use least bright channel to see if the area has
proper exposure or not.
Seems to work fine in tests, but further tweaks are possible.