1
1

Compare commits

...

227 Commits

Author SHA1 Message Date
4469102925 progress 2021-09-10 11:02:06 +02:00
0eb570cbbb progress 2021-09-09 18:55:38 +02:00
e3594c8e65 Merge branch 'master' into parallel-multi-function 2021-09-09 17:47:04 +02:00
bcae5507b8 Merge branch 'temp-geometry-nodes-fields' into parallel-multi-function 2021-09-09 17:45:46 +02:00
29704854df hide new nodes behind feature flag 2021-09-09 11:40:09 +02:00
2920a569b5 progress 2021-09-09 11:19:09 +02:00
015bde6145 Add description for domain property 2021-09-08 09:58:09 -05:00
8d19ceaee7 Rename "dst_hints" to "dst_varrays", update comments 2021-09-08 09:58:01 -05:00
d62cf98164 A few picky changes to comments 2021-09-08 09:51:00 -05:00
655223e4c6 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-08 09:13:22 -05:00
068f012221 initial commit 2021-09-08 15:47:45 +02:00
54cc128a31 Remove changes to the Point Translate node 2021-09-07 13:14:31 -05:00
fa5a9b0f6a Merge branch 'master' into temp-geometry-nodes-fields 2021-09-07 13:05:06 -05:00
cffd4a7ccf Cleanup: Add comment about reusing span for input and output 2021-09-06 20:43:21 -05:00
2012469acc fix exposed string property 2021-09-06 20:54:56 +02:00
713da0fcc4 remove unused function 2021-09-06 20:54:56 +02:00
8ba889a4be Merge branch 'master' into temp-geometry-nodes-fields 2021-09-06 19:42:52 +02:00
5cd7e557ae Cleanup: Comment formatting 2021-09-06 12:39:53 -05:00
584af77101 Add assertion in FieldEvaluator for evaluation state 2021-09-06 12:30:40 -05:00
d53e400454 Merge branch 'temp-geometry-nodes-fields' of git.blender.org:blender into temp-geometry-nodes-fields 2021-09-06 12:24:21 -05:00
64d1fe3450 Update field tests for the newer API 2021-09-06 12:24:17 -05:00
4b091f2eb6 add comments for anonymous attributes 2021-09-06 17:41:15 +02:00
e70f9b0d0e Cleanup: Rename functions
- "try" is not necessary / doesn't really make it clearer
- Fix incorrect name
2021-09-06 10:39:05 -05:00
d2d10e63c9 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-06 17:25:40 +02:00
b51f68d780 Various small cleanups and comment changes 2021-09-06 10:22:21 -05:00
c3adfd6d5d Cleanup: Rename node identifier's and file to include "Input" 2021-09-06 09:50:08 -05:00
edac56b414 more comments 2021-09-06 13:35:06 +02:00
8c76ca6a53 cleanup 2021-09-06 13:24:38 +02:00
0bd2b314e8 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-06 12:55:48 +02:00
2963be588d cleanup 2021-09-06 12:53:03 +02:00
507f2628fd rename Freeze to Capture 2021-09-06 12:05:41 +02:00
c227b4da17 cleanup 2021-09-06 11:45:50 +02:00
605f145aba cleanup FieldNode 2021-09-06 10:58:56 +02:00
deb7cd5783 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-06 10:31:26 +02:00
cb0a78cb8f Merge branch 'master' into temp-geometry-nodes-fields 2021-09-05 23:15:06 -05:00
70e3b6d11f Rename position node file 2021-09-03 15:16:35 -05:00
e041389c68 Fix crash in normal node 2021-09-03 15:15:06 -05:00
38574e37cc Various small cleanups 2021-09-03 13:42:45 -05:00
8186dddec1 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-03 13:21:26 -05:00
47fa00ffef Support fields in the point translate node, add selection input
Because we don't have a "Extract Named Attribute" node currently,
I had to keep the old string input. Otherwise we wouldn't be able to
add versioning.
2021-09-03 13:12:50 -05:00
62f8bb87b7 Add utility methods for field destinations and selection fields 2021-09-03 13:06:54 -05:00
5617cb5500 add some comments 2021-09-03 17:09:43 +02:00
a4d4188c8d rename field related classes 2021-09-03 16:12:47 +02:00
f487dbae97 use GFieldRef where appropriate 2021-09-03 13:29:22 +02:00
94b98d3f88 fix bug when the same field is evaluated twice 2021-09-03 12:54:28 +02:00
32f101c603 add initial Attribute Freeze node 2021-09-03 11:55:22 +02:00
2feed5ce33 support deduplication of normal node 2021-09-03 11:06:50 +02:00
f7b9114eec fix typo 2021-09-03 10:53:06 +02:00
7f017a51bb Merge branch 'master' into temp-geometry-nodes-fields 2021-09-03 10:48:12 +02:00
efbe1ea8c7 Cleanup and improve comment 2021-09-02 23:05:54 -05:00
2b38770ebd Add initial normal node
In contrast with the read-only "normal" attribute on the face domain,
this node gives normalized values for every mesh domain, including
edges and corners. This is preferrable since it's much more predictable
to work with.

The implementation is a little more complicated than I would like,
but could be improved with const access to lazy calculation of normals
on meshes, which is something I've been planning to work on anyway.
2021-09-02 23:00:00 -05:00
5a96096c5e Fix function and define names 2021-09-02 22:55:09 -05:00
ce1519d804 Cleanup: Internally rename "position" to "input_position"
Maybe we'll decide not to do this, for now maybe it's better to be consistent though
2021-09-02 16:24:08 -05:00
bd1375f9cb Cleanup: Rename index node define 2021-09-02 16:20:00 -05:00
af641d0912 Add "Index" node 2021-09-02 16:13:44 -05:00
6c0ae5e890 Allow Set Position node to act on more component types, cleanup 2021-09-02 16:11:30 -05:00
fac0723e92 Add a special case for a selection span
This will probably end up being a utility used elsewhere,
so it's worth adding this case.
2021-09-02 15:57:35 -05:00
68a8a89c49 Reorder input sockets
Since the position is the more important input compared to the
selection, it should be on top
2021-09-02 15:48:36 -05:00
8c9432be6a Cleanup: Remove unnecessary include 2021-09-02 15:47:50 -05:00
6e08de6028 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-02 14:58:26 -05:00
00ed599fe4 fix test 2021-09-02 13:42:54 +02:00
769c7521b5 fix attribute field source deduplication 2021-09-02 13:00:59 +02:00
e23ccfe913 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-02 12:50:26 +02:00
7398076be2 cleanup 2021-09-02 12:48:02 +02:00
ea98f53dcd add initial Position and Set Position node 2021-09-02 12:42:38 +02:00
70cbbd614f get more efficient virtual array from MFParams 2021-09-02 12:41:51 +02:00
95288eba59 improve field evaluator and add geometry context 2021-09-02 12:41:19 +02:00
01037308d5 Cleanup: Change wording of comments 2021-09-01 23:46:25 -05:00
1765e1b20a Merge branch 'master' into temp-geometry-nodes-fields 2021-09-01 23:03:23 -05:00
7cb4665bee new FieldEvaluator utility class 2021-09-01 18:01:48 +02:00
d6bddbef2d add more flexible field evaluation method 2021-09-01 17:00:26 +02:00
fcfec33cf7 Merge branch 'temp-geometry-nodes-fields--fields-jacques' into temp-geometry-nodes-fields 2021-09-01 11:52:37 +02:00
e338ebb720 Merge branch 'temp-geometry-nodes-fields--anonymous-attributes' into temp-geometry-nodes-fields 2021-09-01 11:52:14 +02:00
741deadff4 Merge branch 'master' into temp-geometry-nodes-fields 2021-09-01 11:51:56 +02:00
c1cab4aa68 add common base class for field input/operation 2021-09-01 11:39:18 +02:00
bf6b04bf89 destruct all variables that are not outputs 2021-08-31 13:40:45 +02:00
a275572e0b fix string socket 2021-08-31 13:22:05 +02:00
9ec7e23c44 support modifier inputs again 2021-08-31 12:41:13 +02:00
3c5681c212 support field conversion 2021-08-31 12:40:53 +02:00
28f1e71e2e fix default values 2021-08-31 12:05:11 +02:00
149fd7b65f initial support for function nodes in evaluator 2021-08-31 11:41:53 +02:00
a55c230b8a initial support in evaluator 2021-08-31 11:03:38 +02:00
1f1dc4ade3 field cpp type 2021-08-31 10:22:43 +02:00
d445ee4c73 rename to GField 2021-08-31 09:58:12 +02:00
73e52ab55f Fix index field name for debugging 2021-08-30 17:57:01 -05:00
3fec225931 Cleanup: Rename function, add comments 2021-08-30 17:54:43 -05:00
f0443b8859 Cleanup: Remove incorrect comment 2021-08-30 17:50:47 -05:00
b73f692919 Add a slightly more complicated test 2021-08-30 17:49:38 -05:00
4aeb9dc996 Fix incorrect descruction of output variables 2021-08-30 17:49:20 -05:00
3b12594054 Fix broken test because of stupid mistake 2021-08-30 16:41:34 -05:00
0597e93e5e Add TODO comment 2021-08-30 16:41:17 -05:00
5f003515a1 Add a new (failing) test and some comments 2021-08-30 15:25:04 -05:00
26f8647fea Add name to variable outputs from functions 2021-08-30 15:24:30 -05:00
12f296d800 Add TODO comment 2021-08-30 15:24:15 -05:00
75ec632b61 Fix infinite loop 2021-08-30 14:28:03 -05:00
d6519d4305 Fixes for network traversal, add comments 2021-08-30 14:01:54 -05:00
54cf7eaf92 Cleanup: Make construction of function fields easier to read 2021-08-30 14:01:26 -05:00
d0b1e739b1 Merge branch 'temp-geometry-nodes-fields' into temp-geometry-nodes-fields--fields 2021-08-30 10:59:03 -05:00
b04e2e8877 Merge branch 'master' into temp-geometry-nodes-fields 2021-08-30 10:58:33 -05:00
fb0d5124f2 support anonymous attributes in foreach_attribute 2021-08-30 16:02:58 +02:00
6146a679c9 support creating anonymous attributes 2021-08-30 14:24:18 +02:00
84660c44f0 Refactor procedure building to add proper destructs, to use stacks
This still doesn't really work, but it solves a fundamental problem
with the order I was adding destruct calls for intermediate values.
The current problem is that the variables that a function depends
on (its inputs) are not added first, so basically a problem with the
traversal of the field network.
2021-08-29 12:28:40 -05:00
c398cad059 show procedure parameters in dot graph 2021-08-29 12:46:39 +02:00
35bf6b9790 move dot graph generation into utility class 2021-08-29 12:27:39 +02:00
e5a59e876e slightly improve readability of dot export 2021-08-29 12:09:56 +02:00
d464816c37 Cleanup: Rename variables 2021-08-28 21:20:50 -05:00
007651129a Add comment 2021-08-28 21:18:38 -05:00
1968c9b702 Add another test, add TODO comments 2021-08-28 21:17:21 -05:00
eb54741226 Don't destruct reused inputs after a function call 2021-08-28 18:10:38 -05:00
ce86a518b9 Fix problem building variables for function inuts 2021-08-28 13:29:22 -05:00
09a5ea059f Add a slightly more complicated test
This one doesn't pass, I'll need to debug it
2021-08-28 00:40:58 -05:00
b3cc26bf35 Add another very simple passing test 2021-08-28 00:21:01 -05:00
5f29552be7 Complete handling of field network inputs 2021-08-28 00:05:14 -05:00
6afa55b7e4 Add short comment 2021-08-28 00:02:49 -05:00
37d717fe14 Add FieldInput for input virtual arrays 2021-08-27 15:53:03 -05:00
f193cf66d4 Merge branch 'temp-geometry-nodes-fields' into temp-geometry-nodes-fields--fields 2021-08-27 15:02:33 -05:00
68efa935da Merge branch 'master' into temp-geometry-nodes-fields 2021-08-27 15:01:30 -05:00
1a0fed5d8e Use EXPECT_EQ instead of ASSERT_EQ 2021-08-27 10:55:14 -05:00
c3206bd2a0 Rename Function to FieldFunction 2021-08-27 09:49:36 -05:00
26e7fef920 Merge branch 'temp-geometry-nodes-fields' into temp-geometry-nodes-fields--fields 2021-08-27 09:13:19 -05:00
92e1c8744b Merge branch 'master' into temp-geometry-nodes-fields 2021-08-27 09:12:42 -05:00
cfe8832dd3 Revert "Rename existing boolean node to mesh boolean"
This reverts commit 6a5b048658.

I'm in the wrong branch again!
2021-08-26 23:18:32 -05:00
6a5b048658 Rename existing boolean node to mesh boolean 2021-08-26 23:16:10 -05:00
ad0dbde653 Merge branch 'master' into temp-geometry-nodes-fields 2021-08-26 23:15:27 -05:00
f25c1b4950 Revert "Rename existing boolean node to "Mesh Boolean""
This reverts commit 469f752b80.

I thought I was in a different branch.
2021-08-26 23:15:02 -05:00
469f752b80 Rename existing boolean node to "Mesh Boolean" 2021-08-26 23:12:50 -05:00
55f2867db3 Merge branch 'master' into temp-geometry-nodes-fields--fields 2021-08-26 23:11:03 -05:00
80429002d7 Add an index input function test
This one fails because `tot_initialized_` is 0 in the procedure
evaluator. I'm not quite sure what that means yet.
2021-08-26 16:32:04 -05:00
0910b76be3 Add a high-level description to the header file 2021-08-26 15:28:41 -05:00
1ccfd6842b Refactor field storage again, to allow reuse of function outputs 2021-08-26 15:23:53 -05:00
0a0360c8cd Fix constant input test 2021-08-26 11:19:58 -05:00
be0201259a Merge branch 'temp-geometry-nodes-fields' into temp-geometry-nodes-fields--fields 2021-08-26 10:43:32 -05:00
ef2a48329d Merge branch 'master' into temp-geometry-nodes-fields 2021-08-26 10:41:59 -05:00
81a0c384da Experimental simplification: non-virtual multi-function-only fields
The stupidly simple test doesn't pass anymore.
2021-08-25 17:39:19 -05:00
70eaba3cb1 Basic constant input test passes 2021-08-25 16:23:46 -05:00
7e39e78259 Merge branch 'master' into temp-geometry-nodes-fields--fields 2021-08-25 14:28:57 -05:00
c2122c39ae Remove GArray, futher improvement 2021-08-25 09:55:34 -05:00
fb26ee8a7e Initial crappy code that doesn't do anything and wouldn't compile 2021-08-24 17:35:46 -05:00
001072721f Merge branch 'master' into temp-geometry-nodes-fields 2021-08-24 13:45:48 -05:00
65a1ec89ba Merge branch 'temp-geometry-nodes-fields' into temp-geometry-nodes-fields--anonymous-attributes 2021-08-24 17:56:36 +02:00
ef9fbf258b use attribute id in more places 2021-08-24 17:53:15 +02:00
a448949f25 more uses of attribute id 2021-08-24 17:30:56 +02:00
e3232f987a initial attribute id ref 2021-08-24 17:11:24 +02:00
7ebc3140bb Merge branch 'master' into temp-geometry-nodes-fields 2021-08-23 15:12:04 -05:00
c42ceef040 initial anonymous attributes implementation 2021-08-23 18:40:29 +02:00
acc2e8afa9 Merge branch 'master' into temp-geometry-nodes-fields 2021-08-23 15:58:16 +02:00
04bb1bda32 fix comment 2021-08-23 15:57:50 +02:00
eed93aaa07 make dot output more compact 2021-08-22 22:34:25 +02:00
fc0bb6cdee avoid allocating index array in some cases 2021-08-22 20:29:52 +02:00
10f2ad1556 add return instruction and initial procedure validation 2021-08-22 20:16:27 +02:00
6d77b87b13 support span buffer reuse 2021-08-22 15:07:23 +02:00
2101b46802 fix comment 2021-08-22 12:49:22 +02:00
34f6765630 Merge branch 'master' into temp-multi-function-procedure 2021-08-22 12:21:52 +02:00
c58d1acba8 improve naming 2021-08-20 14:29:01 +02:00
0ee79f304e cleanup 2021-08-20 13:38:38 +02:00
e642de3d6f Merge branch 'master' into temp-multi-function-procedure 2021-08-20 13:37:32 +02:00
eca5a8b695 Merge branch 'master' into temp-multi-function-procedure 2021-08-20 12:48:18 +02:00
79c79f3c70 cleanup 2021-08-20 11:37:23 +02:00
a9970d3cb9 bring back clamping in math node 2021-08-20 10:44:29 +02:00
b812f289f5 Merge branch 'master' into mf-procedure 2021-08-20 10:36:04 +02:00
215ce0fb57 fix 2021-08-19 18:19:15 +02:00
5154598845 Merge branch 'master' into mf-procedure 2021-08-19 18:11:51 +02:00
1ee80d792c cleanup 2021-08-19 18:09:52 +02:00
95284d2f1e bring back function nodes 2021-08-19 18:07:36 +02:00
7281f3eb56 start bringing back function nodes 2021-08-19 17:28:15 +02:00
607ef8f6c5 pull out multi function network 2021-08-19 16:49:00 +02:00
1ce640cc0b test vector processing 2021-08-19 16:24:32 +02:00
7bed18fdb1 support creating loops with builder 2021-08-19 15:33:35 +02:00
c827b50d40 add dummy instruction type 2021-08-19 14:06:43 +02:00
3c7e3c8e44 improve naming 2021-08-19 13:46:19 +02:00
98e38ce4f3 cleanup 2021-08-19 13:44:15 +02:00
132cf268c0 add comments 2021-08-19 13:36:52 +02:00
fd7edc9b05 cleanup 2021-08-19 13:28:15 +02:00
ecf7c90840 remove redundant utilties 2021-08-19 13:15:11 +02:00
d78a530af1 initial procedure builder 2021-08-19 13:09:41 +02:00
3ebe61db9f support evaluation on one 2021-08-19 11:22:57 +02:00
86c2f139c6 cleanup 2021-08-19 10:09:02 +02:00
3596c348eb cleanup 2021-08-19 10:07:51 +02:00
41a81474e4 refactor procedure executor 2021-08-18 20:19:12 +02:00
aa2822d137 cleanup 2021-08-18 20:18:19 +02:00
55b333d3e3 Merge branch 'master' into mf-procedure 2021-08-18 16:14:32 +02:00
00cfad8578 add utility method 2021-08-18 10:00:51 +02:00
1891c956e5 refactor variable store 2021-08-17 17:24:01 +02:00
249c050757 add single test 2021-08-17 15:27:46 +02:00
a0081046b6 cleanup instruction scheduling 2021-08-17 15:01:27 +02:00
635f73b7f1 fixes after merge 2021-08-17 14:00:06 +02:00
74fcd50e2f Merge branch 'master' into mf-procedure 2021-08-17 13:44:25 +02:00
b04a2a7be7 add utility 2021-06-13 14:42:22 +02:00
083671e8ac progress 2021-06-13 14:25:23 +02:00
78ea401e19 Merge branch 'master' into mf-procedure 2021-06-13 14:13:22 +02:00
f3ca987bce start constructing procedure from node tree 2021-06-11 13:39:38 +02:00
2245add9f8 Merge branch 'master' into mf-procedure 2021-06-11 12:59:12 +02:00
31004d7fac start with creating procedure for node tree 2021-05-31 10:51:34 +02:00
3d3f66ed41 fix merge conflicts 2021-05-29 12:14:48 +02:00
c9c0195da5 Merge branch 'master' into mf-procedure 2021-05-29 12:07:13 +02:00
70c0403858 fixes 2021-03-27 22:58:44 +01:00
8d4de82c7f initial network to procedure 2021-03-27 22:30:53 +01:00
22c51c2d51 cleanup 2021-03-27 21:22:28 +01:00
158bd7c6a0 cleanup 2021-03-27 21:18:48 +01:00
4a28d0b583 another check 2021-03-27 15:52:13 +01:00
0501e6e693 fix memory leak in test 2021-03-27 15:45:59 +01:00
8450ac09c1 comment containing things to check 2021-03-27 15:36:08 +01:00
1af00015e8 initial destruct support 2021-03-27 15:29:02 +01:00
b44c3a3125 count initializations 2021-03-27 15:12:36 +01:00
d729f1ca37 cleanup 2021-03-27 15:08:21 +01:00
0fc9f00c14 start extracting container 2021-03-27 14:53:39 +01:00
6c9b339af7 refactor variable store 2021-03-27 14:46:53 +01:00
b7a976af01 branch test 2021-03-27 14:07:23 +01:00
a689037917 initial branch instruction 2021-03-27 13:48:18 +01:00
313403c1f1 support mutable params 2021-03-27 13:33:42 +01:00
60409b8823 simplify 2021-03-27 13:19:46 +01:00
773dc2ec94 improve dot graph 2021-03-27 13:14:19 +01:00
2a98c5d06b initial execution 2021-03-27 13:06:50 +01:00
6d1b4ce3c6 simplify 2021-03-27 12:06:40 +01:00
0b2d961b70 move executor to separate file 2021-03-27 11:55:51 +01:00
d553b70470 Merge branch 'master' into mf-procedure 2021-03-27 11:52:12 +01:00
6954f2cdd7 dot export 2021-03-24 18:46:17 +01:00
8cc832110a more 2021-03-24 18:23:36 +01:00
7b8c54b5a1 more 2021-03-24 17:51:37 +01:00
e850d175b5 more 2021-03-24 17:42:18 +01:00
326f79d59b more 2021-03-24 17:39:22 +01:00
ec4954ece2 destructor 2021-03-24 17:34:14 +01:00
b30e782c82 more stuff 2021-03-24 17:30:53 +01:00
e34fe5d28e add destruct instruction 2021-03-24 16:49:52 +01:00
8581a062f1 Merge branch 'master' into mf-procedure 2021-03-24 16:47:48 +01:00
b43971e5e9 add executor class 2021-03-23 16:20:40 +01:00
855382170e initial mf procedure data structure 2021-03-23 16:18:23 +01:00
14 changed files with 592 additions and 140 deletions

View File

@@ -653,7 +653,8 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
return;
}
}
/* This fallback is used even when one of the inputs could be optimized. It's probably not worth
/* This fallback is used even when one of the inputs could be optimized. It's probably not
worth
* it to optimize just one of the inputs, because then the compiler still has to call into
* unknown code, which inhibits many compiler optimizations. */
func(varray1, varray2);

View File

@@ -34,9 +34,11 @@ set(SRC
intern/generic_virtual_vector_array.cc
intern/multi_function.cc
intern/multi_function_builder.cc
intern/multi_function_parallel.cc
intern/multi_function_procedure.cc
intern/multi_function_procedure_builder.cc
intern/multi_function_procedure_executor.cc
intern/multi_function_procedure_optimization.cc
FN_cpp_type.hh
FN_cpp_type_make.hh
@@ -54,9 +56,11 @@ set(SRC
FN_multi_function_data_type.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
FN_multi_function_parallel.hh
FN_multi_function_procedure.hh
FN_multi_function_procedure_builder.hh
FN_multi_function_procedure_executor.hh
FN_multi_function_procedure_optimization.hh
FN_multi_function_signature.hh
)
@@ -64,6 +68,22 @@ set(LIB
bf_blenlib
)
if(WITH_TBB)
add_definitions(-DWITH_TBB)
if(WIN32)
# TBB includes Windows.h which will define min/max macros
# that will collide with the stl versions.
add_definitions(-DNOMINMAX)
endif()
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
)
list(APPEND LIB
${TBB_LIBRARIES}
)
endif()
blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)

View File

@@ -911,4 +911,50 @@ template<typename T> class GVMutableArray_Typed {
}
};
class GVArray_For_SlicedGVArray : public GVArray {
protected:
const GVArray &varray_;
int64_t offset_;
public:
GVArray_For_SlicedGVArray(const GVArray &varray, const IndexRange slice)
: GVArray(varray.type(), slice.size()), varray_(varray), offset_(slice.start())
{
BLI_assert(slice.one_after_last() <= varray.size());
}
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
};
/**
* Utility class to create the "best" sliced virtual array.
*/
class GVArray_Slice {
private:
const GVArray *varray_;
/* Of these optional virtual arrays, at most one is constructed at any time. */
std::optional<GVArray_For_GSpan> varray_span_;
std::optional<GVArray_For_SingleValue> varray_single_;
std::optional<GVArray_For_SlicedGVArray> varray_any_;
public:
GVArray_Slice(const GVArray &varray, const IndexRange slice);
const GVArray &operator*()
{
return *varray_;
}
const GVArray *operator->()
{
return varray_;
}
operator const GVArray &()
{
return *varray_;
}
};
} // namespace blender::fn

View File

@@ -0,0 +1,39 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup fn
*/
#include "FN_multi_function.hh"
namespace blender::fn {
class ParallelMultiFunction : public MultiFunction {
private:
const MultiFunction &fn_;
const int64_t grain_size_;
bool threading_supported_;
public:
ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size);
void call(IndexMask mask, MFParams params, MFContext context) const override;
};
} // namespace blender::fn

View File

@@ -42,6 +42,55 @@ enum class MFInstructionType {
Return,
};
/**
* An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction
* can be inserted.
*/
class MFInstructionCursor {
public:
enum Type {
None,
Entry,
Call,
Destruct,
Branch,
Dummy,
};
private:
Type type_ = None;
MFInstruction *instruction_ = nullptr;
/* Only used when it is a branch instruction. */
bool branch_output_ = false;
public:
MFInstructionCursor() = default;
MFInstructionCursor(MFCallInstruction &instruction);
MFInstructionCursor(MFDestructInstruction &instruction);
MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output);
MFInstructionCursor(MFDummyInstruction &instruction);
static MFInstructionCursor ForEntry();
MFInstruction *next(MFProcedure &procedure) const;
void set_next(MFProcedure &procedure, MFInstruction *new_instruction) const;
MFInstruction *instruction() const;
Type type() const;
friend bool operator==(const MFInstructionCursor &a, const MFInstructionCursor &b)
{
return a.type_ == b.type_ && a.instruction_ == b.instruction_ &&
a.branch_output_ == b.branch_output_;
}
friend bool operator!=(const MFInstructionCursor &a, const MFInstructionCursor &b)
{
return !(a == b);
}
};
/**
* A variable is similar to a virtual register in other libraries. During evaluation, every is
* either uninitialized or contains a value for every index (remember, a multi-function procedure
@@ -73,7 +122,7 @@ class MFVariable : NonCopyable, NonMovable {
class MFInstruction : NonCopyable, NonMovable {
protected:
MFInstructionType type_;
Vector<MFInstruction *> prev_;
Vector<MFInstructionCursor> prev_;
friend MFProcedure;
friend MFCallInstruction;
@@ -89,8 +138,7 @@ class MFInstruction : NonCopyable, NonMovable {
* Other instructions that come before this instruction. There can be multiple previous
* instructions when branching is used in the procedure.
*/
Span<MFInstruction *> prev();
Span<const MFInstruction *> prev() const;
Span<MFInstructionCursor> prev() const;
};
/**
@@ -246,6 +294,9 @@ class MFProcedure : NonCopyable, NonMovable {
Span<MFVariable *> variables();
Span<const MFVariable *> variables() const;
Span<MFDestructInstruction *> destruct_instructions();
Span<const MFDestructInstruction *> destruct_instructions() const;
std::string to_dot() const;
bool validate() const;
@@ -275,6 +326,50 @@ using MFDestructInstruction = fn::MFDestructInstruction;
using MFProcedure = fn::MFProcedure;
} // namespace multi_function_procedure_types
/* --------------------------------------------------------------------
* MFInstructionCursor inline methods.
*/
inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction)
: type_(Call), instruction_(&instruction)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction)
: type_(Destruct), instruction_(&instruction)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction,
bool branch_output)
: type_(Branch), instruction_(&instruction), branch_output_(branch_output)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction)
: type_(Dummy), instruction_(&instruction)
{
}
inline MFInstructionCursor MFInstructionCursor::ForEntry()
{
MFInstructionCursor cursor;
cursor.type_ = Type::Entry;
return cursor;
}
inline MFInstruction *MFInstructionCursor::instruction() const
{
/* This isn't really const correct unfortunately, because to make it correct we'll need a const
* version of #MFInstructionCursor. */
return instruction_;
}
inline MFInstructionCursor::Type MFInstructionCursor::type() const
{
return type_;
}
/* --------------------------------------------------------------------
* MFVariable inline methods.
*/
@@ -308,12 +403,7 @@ inline MFInstructionType MFInstruction::type() const
return type_;
}
inline Span<MFInstruction *> MFInstruction::prev()
{
return prev_;
}
inline Span<const MFInstruction *> MFInstruction::prev() const
inline Span<MFInstructionCursor> MFInstruction::prev() const
{
return prev_;
}
@@ -449,4 +539,14 @@ inline Span<const MFVariable *> MFProcedure::variables() const
return variables_;
}
inline Span<MFDestructInstruction *> MFProcedure::destruct_instructions()
{
return destruct_instructions_;
}
inline Span<const MFDestructInstruction *> MFProcedure::destruct_instructions() const
{
return destruct_instructions_;
}
} // namespace blender::fn

View File

@@ -24,31 +24,6 @@
namespace blender::fn {
/**
* An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction
* can be inserted.
*/
class MFInstructionCursor {
private:
MFInstruction *instruction_ = nullptr;
/* Only used when it is a branch instruction. */
bool branch_output_ = false;
/* Only used when instruction is null. */
bool is_entry_ = false;
public:
MFInstructionCursor() = default;
MFInstructionCursor(MFCallInstruction &instruction);
MFInstructionCursor(MFDestructInstruction &instruction);
MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output);
MFInstructionCursor(MFDummyInstruction &instruction);
static MFInstructionCursor Entry();
void insert(MFProcedure &procedure, MFInstruction *new_instruction);
};
/**
* Utility class to build a #MFProcedure.
*/
@@ -64,7 +39,7 @@ class MFProcedureBuilder {
struct Loop;
MFProcedureBuilder(MFProcedure &procedure,
MFInstructionCursor initial_cursor = MFInstructionCursor::Entry());
MFInstructionCursor initial_cursor = MFInstructionCursor::ForEntry());
MFProcedureBuilder(Span<MFProcedureBuilder *> builders);
@@ -121,38 +96,6 @@ struct MFProcedureBuilder::Loop {
MFDummyInstruction *end = nullptr;
};
/* --------------------------------------------------------------------
* MFInstructionCursor inline methods.
*/
inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction)
: instruction_(&instruction)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction)
: instruction_(&instruction)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction,
bool branch_output)
: instruction_(&instruction), branch_output_(branch_output)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction)
: instruction_(&instruction)
{
}
inline MFInstructionCursor MFInstructionCursor::Entry()
{
MFInstructionCursor cursor;
cursor.is_entry_ = true;
return cursor;
}
/* --------------------------------------------------------------------
* MFProcedureBuilder inline methods.
*/
@@ -253,7 +196,7 @@ inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable)
inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction)
{
for (MFInstructionCursor &cursor : cursors_) {
cursor.insert(*procedure_, instruction);
cursor.set_next(*procedure_, instruction);
}
}

View File

@@ -0,0 +1,29 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup fn
*/
#include "FN_multi_function_procedure.hh"
namespace blender::fn::procedure_optimization {
void move_destructs_up(MFProcedure &procedure);
}

View File

@@ -18,8 +18,13 @@
#include "BLI_multi_value_map.hh"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_timeit.hh"
#include "BLI_vector_set.hh"
#include "FN_field.hh"
#include "FN_multi_function_parallel.hh"
#include "FN_multi_function_procedure_optimization.hh"
#include "FN_field.hh"
namespace blender::fn {
@@ -183,8 +188,8 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
const Span<GField> operation_inputs = operation.inputs();
if (field_with_index.current_input_index < operation_inputs.size()) {
/* Not all inputs are handled yet. Push the next input field to the stack and increment the
* input index. */
/* Not all inputs are handled yet. Push the next input field to the stack and increment
* the input index. */
fields_to_check.push({operation_inputs[field_with_index.current_input_index]});
field_with_index.current_input_index++;
}
@@ -248,8 +253,8 @@ struct PartiallyInitializedArray : NonCopyable, NonMovable {
};
/**
* Evaluate fields in the given context. If possible, multiple fields should be evaluated together,
* because that can be more efficient when they share common sub-fields.
* Evaluate fields in the given context. If possible, multiple fields should be evaluated
* together, because that can be more efficient when they share common sub-fields.
*
* \param scope: The resource scope that owns data that makes up the output virtual arrays. Make
* sure the scope is not destructed when the output virtual arrays are still used.
@@ -271,6 +276,7 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
const FieldContext &context,
Span<GVMutableArray *> dst_varrays)
{
Vector<const GVArray *> r_varrays(fields_to_evaluate.size(), nullptr);
const int array_size = mask.min_array_size();
@@ -290,8 +296,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
Vector<const GVArray *> field_context_inputs = get_field_context_inputs(
scope, mask, context, field_tree_info.deduplicated_field_inputs);
/* Finish fields that output an input varray directly. For those we don't have to do any further
* processing. */
/* Finish fields that output an input varray directly. For those we don't have to do any
* further processing. */
for (const int out_index : fields_to_evaluate.index_range()) {
const GFieldRef &field = fields_to_evaluate[out_index];
if (!field.node().is_input()) {
@@ -333,8 +339,14 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
MFProcedure procedure;
build_multi_function_procedure_for_fields(
procedure, scope, field_tree_info, varying_fields_to_evaluate);
// std::cout << procedure.to_dot() << "\n";
// fn::procedure_optimization::move_destructs_up(procedure);
// std::cout << procedure.to_dot() << "\n";
MFProcedureExecutor procedure_executor{"Procedure", procedure};
MFParamsBuilder mf_params{procedure_executor, array_size};
fn::ParallelMultiFunction parallel_fn{procedure_executor, 10000};
const MultiFunction &fn_to_execute = parallel_fn;
MFParamsBuilder mf_params{fn_to_execute, array_size};
MFContextBuilder mf_context;
/* Provide inputs to the procedure executor. */
@@ -376,7 +388,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
mf_params.add_uninitialized_single_output(span);
}
procedure_executor.call(mask, mf_params, mf_context);
SCOPED_TIMER(__func__);
fn_to_execute.call(mask, mf_params, mf_context);
}
/* Evaluate constant fields if necessary. */
@@ -419,8 +432,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
procedure_executor.call(IndexRange(1), mf_params, mf_context);
}
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has
* written the computed data in the right place already. */
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above
* has written the computed data in the right place already. */
if (!dst_varrays.is_empty()) {
for (const int out_index : fields_to_evaluate.index_range()) {
GVMutableArray *output_varray = get_dst_varray_if_available(out_index);

View File

@@ -387,4 +387,43 @@ void GVMutableArray_GSpan::disable_not_applied_warning()
show_not_saved_warning_ = false;
}
/* --------------------------------------------------------------------
* GVArray_For_SlicedGVArray.
*/
void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const
{
varray_.get(index + offset_, r_value);
}
void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, void *r_value) const
{
varray_.get_to_uninitialized(index + offset_, r_value);
}
/* --------------------------------------------------------------------
* GVArray_Slice.
*/
GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice)
{
const CPPType &type = varray.type();
if (varray.is_span()) {
const GSpan span = varray.get_internal_span();
varray_span_.emplace(span.slice(slice.start(), slice.size()));
varray_ = &*varray_span_;
}
else if (varray.is_single()) {
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
varray_->get_internal_single_to_uninitialized(buffer);
varray_single_.emplace(type, slice.size(), buffer);
type.destruct(buffer);
varray_ = &*varray_single_;
}
else {
varray_any_.emplace(varray, slice);
varray_ = &*varray_any_;
}
}
} // namespace blender::fn

View File

@@ -0,0 +1,109 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "FN_multi_function_parallel.hh"
#include "BLI_task.hh"
#include <mutex>
namespace blender::fn {
ParallelMultiFunction::ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size)
: fn_(fn), grain_size_(grain_size)
{
this->set_signature(&fn.signature());
threading_supported_ = true;
for (const int param_index : fn.param_indices()) {
const MFParamType param_type = fn.param_type(param_index);
if (param_type.data_type().category() == MFDataType::Vector) {
threading_supported_ = false;
break;
}
}
}
void ParallelMultiFunction::call(IndexMask mask, MFParams params, MFContext context) const
{
if (mask.size() <= grain_size_ || !threading_supported_) {
fn_.call(mask, params, context);
return;
}
threading::parallel_for(mask.index_range(), grain_size_, [&](const IndexRange range) {
const int size = range.size();
IndexMask original_sub_mask{mask.indices().slice(range)};
const int64_t offset = original_sub_mask.indices().first();
const int64_t slice_size = original_sub_mask.indices().last() - offset + 1;
const IndexRange slice_range{offset, slice_size};
IndexMask sub_mask;
Vector<int64_t> sub_mask_indices;
if (original_sub_mask.is_range()) {
sub_mask = IndexMask(size);
}
else {
sub_mask_indices.resize(size);
for (const int i : IndexRange(size)) {
sub_mask_indices[i] = original_sub_mask[i] - offset;
}
sub_mask = sub_mask_indices.as_span();
}
MFParamsBuilder sub_params{fn_, sub_mask.min_array_size()};
ResourceScope scope;
// static std::mutex mutex;
// {
// std::lock_guard lock{mutex};
// std::cout << range << " " << sub_mask.min_array_size() << "\n";
// }
for (const int param_index : fn_.param_indices()) {
const MFParamType param_type = fn_.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const GVArray &varray = params.readonly_single_input(param_index);
const GVArray &sliced_varray = scope.construct<GVArray_Slice>(
"sliced varray", varray, slice_range);
sub_params.add_readonly_single_input(sliced_varray);
break;
}
case MFParamType::SingleMutable: {
const GMutableSpan span = params.single_mutable(param_index);
const GMutableSpan sliced_span = span.slice(slice_range.start(), slice_range.size());
sub_params.add_single_mutable(sliced_span);
break;
}
case MFParamType::SingleOutput: {
const GMutableSpan span = params.uninitialized_single_output(param_index);
const GMutableSpan sliced_span = span.slice(slice_range.start(), slice_range.size());
sub_params.add_uninitialized_single_output(sliced_span);
break;
}
case MFParamType::VectorInput:
case MFParamType::VectorMutable:
case MFParamType::VectorOutput: {
BLI_assert_unreachable();
break;
}
}
}
fn_.call(sub_mask, sub_params, context);
});
}
} // namespace blender::fn

View File

@@ -21,6 +21,65 @@
namespace blender::fn {
void MFInstructionCursor::set_next(MFProcedure &procedure, MFInstruction *new_instruction) const
{
switch (type_) {
case Type::None: {
break;
}
case Type::Entry: {
procedure.set_entry(*new_instruction);
break;
}
case Type::Call: {
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case Type::Branch: {
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_);
if (branch_output_) {
branch_instruction.set_branch_true(new_instruction);
}
else {
branch_instruction.set_branch_false(new_instruction);
}
break;
}
case Type::Destruct: {
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case Type::Dummy: {
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
break;
}
}
}
MFInstruction *MFInstructionCursor::next(MFProcedure &procedure) const
{
switch (type_) {
case Type::None:
return nullptr;
case Type::Entry:
return procedure.entry();
case Type::Call:
return static_cast<MFCallInstruction *>(instruction_)->next();
case Type::Branch: {
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_);
if (branch_output_) {
return branch_instruction.branch_true();
}
return branch_instruction.branch_false();
}
case Type::Destruct:
return static_cast<MFDestructInstruction *>(instruction_)->next();
case Type::Dummy:
return static_cast<MFDummyInstruction *>(instruction_)->next();
}
return nullptr;
}
void MFVariable::set_name(std::string name)
{
name_ = std::move(name);
@@ -29,10 +88,10 @@ void MFVariable::set_name(std::string name)
void MFCallInstruction::set_next(MFInstruction *instruction)
{
if (next_ != nullptr) {
next_->prev_.remove_first_occurrence_and_reorder(this);
next_->prev_.remove_first_occurrence_and_reorder(*this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
instruction->prev_.append(*this);
}
next_ = instruction;
}
@@ -71,10 +130,10 @@ void MFBranchInstruction::set_condition(MFVariable *variable)
void MFBranchInstruction::set_branch_true(MFInstruction *instruction)
{
if (branch_true_ != nullptr) {
branch_true_->prev_.remove_first_occurrence_and_reorder(this);
branch_true_->prev_.remove_first_occurrence_and_reorder({*this, true});
}
if (instruction != nullptr) {
instruction->prev_.append(this);
instruction->prev_.append({*this, true});
}
branch_true_ = instruction;
}
@@ -82,10 +141,10 @@ void MFBranchInstruction::set_branch_true(MFInstruction *instruction)
void MFBranchInstruction::set_branch_false(MFInstruction *instruction)
{
if (branch_false_ != nullptr) {
branch_false_->prev_.remove_first_occurrence_and_reorder(this);
branch_false_->prev_.remove_first_occurrence_and_reorder({*this, false});
}
if (instruction != nullptr) {
instruction->prev_.append(this);
instruction->prev_.append({*this, false});
}
branch_false_ = instruction;
}
@@ -104,10 +163,10 @@ void MFDestructInstruction::set_variable(MFVariable *variable)
void MFDestructInstruction::set_next(MFInstruction *instruction)
{
if (next_ != nullptr) {
next_->prev_.remove_first_occurrence_and_reorder(this);
next_->prev_.remove_first_occurrence_and_reorder(*this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
instruction->prev_.append(*this);
}
next_ = instruction;
}
@@ -115,10 +174,10 @@ void MFDestructInstruction::set_next(MFInstruction *instruction)
void MFDummyInstruction::set_next(MFInstruction *instruction)
{
if (next_ != nullptr) {
next_->prev_.remove_first_occurrence_and_reorder(this);
next_->prev_.remove_first_occurrence_and_reorder(*this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
instruction->prev_.append(*this);
}
next_ = instruction;
}
@@ -420,7 +479,11 @@ MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction
Set<const MFInstruction *> checked_instructions;
Stack<const MFInstruction *> instructions_to_check;
instructions_to_check.push_multiple(target_instruction.prev_);
for (const MFInstructionCursor &cursor : target_instruction.prev_) {
if (cursor.instruction() != nullptr) {
instructions_to_check.push(cursor.instruction());
}
}
while (!instructions_to_check.is_empty()) {
const MFInstruction &instruction = *instructions_to_check.pop();
@@ -467,7 +530,11 @@ MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction
if (&instruction == entry_) {
check_entry_instruction();
}
instructions_to_check.push_multiple(instruction.prev_);
for (const MFInstructionCursor &cursor : instruction.prev_) {
if (cursor.instruction() != nullptr) {
instructions_to_check.push(cursor.instruction());
}
}
}
}
@@ -607,13 +674,10 @@ class MFProcedureDotExport {
bool has_to_be_block_begin(const MFInstruction &instruction)
{
if (procedure_.entry() == &instruction) {
return true;
}
if (instruction.prev().size() != 1) {
return true;
}
if (instruction.prev()[0]->type() == MFInstructionType::Branch) {
if (instruction.prev()[0].type() == MFInstructionCursor::Type::Branch) {
return true;
}
return false;
@@ -623,7 +687,7 @@ class MFProcedureDotExport {
{
const MFInstruction *current = &representative;
while (!this->has_to_be_block_begin(*current)) {
current = current->prev()[0];
current = current->prev()[0].instruction();
if (current == &representative) {
/* There is a loop without entry or exit, just break it up here. */
break;

View File

@@ -18,50 +18,6 @@
namespace blender::fn {
void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction)
{
if (instruction_ == nullptr) {
if (is_entry_) {
procedure.set_entry(*new_instruction);
}
else {
/* The cursors points at nothing, nothing to do. */
}
}
else {
switch (instruction_->type()) {
case MFInstructionType::Call: {
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case MFInstructionType::Branch: {
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(
instruction_);
if (branch_output_) {
branch_instruction.set_branch_true(new_instruction);
}
else {
branch_instruction.set_branch_false(new_instruction);
}
break;
}
case MFInstructionType::Destruct: {
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case MFInstructionType::Dummy: {
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case MFInstructionType::Return: {
/* It shouldn't be possible to build a cursor that points to a return instruction. */
BLI_assert_unreachable();
break;
}
}
}
}
void MFProcedureBuilder::add_destruct(MFVariable &variable)
{
MFDestructInstruction &instruction = procedure_->new_destruct_instruction();

View File

@@ -0,0 +1,66 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "FN_multi_function_procedure_optimization.hh"
namespace blender::fn::procedure_optimization {
static bool uses_variable(const MFInstruction &instr, const MFVariable &variable)
{
switch (instr.type()) {
case MFInstructionType::Branch:
return static_cast<const MFBranchInstruction &>(instr).condition() == &variable;
case MFInstructionType::Call:
return static_cast<const MFCallInstruction &>(instr).params().contains(&variable);
case MFInstructionType::Destruct:
return static_cast<const MFDestructInstruction &>(instr).variable() == &variable;
default:
return false;
}
}
void move_destructs_up(MFProcedure &procedure)
{
for (MFDestructInstruction *destruct_instr : procedure.destruct_instructions()) {
MFVariable *variable = destruct_instr->variable();
if (variable == nullptr) {
continue;
}
MFInstruction *last_use_in_block_instr = nullptr;
MFInstruction *current_instr = destruct_instr;
while (current_instr->prev().size() == 1) {
current_instr = current_instr->prev()[0].instruction();
if (current_instr == nullptr) {
break;
}
if (uses_variable(*current_instr, *variable)) {
last_use_in_block_instr = current_instr;
break;
}
}
if (last_use_in_block_instr == nullptr) {
continue;
}
if (last_use_in_block_instr->type() == MFInstructionType::Call) {
MFCallInstruction &call_instr = static_cast<MFCallInstruction &>(*last_use_in_block_instr);
destruct_instr->prev()[0].set_next(procedure, destruct_instr->next());
destruct_instr->set_next(call_instr.next());
call_instr.set_next(destruct_instr);
}
}
}
} // namespace blender::fn::procedure_optimization

View File

@@ -2,8 +2,11 @@
#include "testing/testing.h"
#include "BLI_timeit.hh"
#include "FN_multi_function.hh"
#include "FN_multi_function_builder.hh"
#include "FN_multi_function_parallel.hh"
#include "FN_multi_function_test_common.hh"
namespace blender::fn::tests {
@@ -328,5 +331,29 @@ TEST(multi_function, CustomMF_Convert)
EXPECT_EQ(outputs[2], 9);
}
TEST(multi_function, Parallel)
{
CustomMF_SI_SI_SO<float, float, float> add_fn{
"add", [](float a, float b) { return std::tan(std::sin(a)) * std::tanh(std::cos(b)); }};
ParallelMultiFunction parallel_fn{add_fn, int64_t(1e5)};
const MultiFunction &fn_to_evaluate = parallel_fn;
const int amount = 1e8;
Array<float> inputs_a(amount, 1);
Array<float> inputs_b(amount, 1);
Array<float> outputs(amount, 1);
for (int i = 0; i < 10; i++) {
SCOPED_TIMER(__func__);
MFParamsBuilder params(fn_to_evaluate, amount);
params.add_readonly_single_input(inputs_a.as_span());
params.add_readonly_single_input(inputs_b.as_span());
params.add_uninitialized_single_output(outputs.as_mutable_span());
MFContextBuilder context;
fn_to_evaluate.call(IndexRange(amount), params, context);
}
}
} // namespace
} // namespace blender::fn::tests