libliquidfun-sys 0.4.0

A thin Rust ffi wrapper on top of Box2D and LiquidFun. Built using autocxx.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
@
@ Copyright (c) 2014 Google, Inc.
@
@ This software is provided 'as-is', without any express or implied
@ warranty.  In no event will the authors be held liable for any damages
@ arising from the use of this software.
@ Permission is granted to anyone to use this software for any purpose,
@ including commercial applications, and to alter it and redistribute it
@ freely, subject to the following restrictions:
@ 1. The origin of this software must not be misrepresented; you must not
@ claim that you wrote the original software. If you use this software
@ in a product, an acknowledgment in the product documentation would be
@ appreciated but is not required.
@ 2. Altered source versions must be plainly marked as such, and must not be
@ misrepresented as being the original software.
@ 3. This notice may not be removed or altered from any source distribution.
@
        .text
        .syntax   unified

        .balign   4
        .global   CalculateTags_Simd
        .thumb_func

CalculateTags_Simd:
        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        @
        @ int CalculateTags_Simd(const b2Vec2* positions,
        @                        int count,
        @                        const float& inverseDiameter,
        @                        uint32* outTags)
        @
        @  r0: *positions
        @  r1: count
        @  r2: &inverseDiameter
        @  r3: *outTags
        @
        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

        @  q0 == x
        @  q1 == y
        @  q2 ==
        @  q3 ==
        @  q4 ==
        @  q5 ==
        @  q6 ==
        @  q7 ==
        @  q8 ==
        @  q9 ==
        @ q10 ==
        @ q11 ==
        @ q12 == inverseDiameter
        @ q13 == xScale
        @ q14 == xOffset
        @ q15 == yOffset

        @ Load constants. Literals are > 32, so must load as integers first.
        vld1.f32          {d24[],d25[]}, [r2] @ q12 = inverseDiameter
        vmov.i32          q13, #0x100   @ q13 = xScale = 1 << 8
        vmov.i32          q14, #0x80000 @ q14 = xOffset = (1 << 8) * (1 << 11)
                                        @               = (1 << 19) = 524288
        vmov.i32          q15, #0x800   @ q15 = xScale = 1 << 11 = 2048
        vcvt.f32.u32      q13, q13      @ convert to float
        vcvt.f32.u32      q14, q14
        vcvt.f32.u32      q15, q15

        @ Calculate tags four at a time, from positions.
.L_CalculateTags_MainLoop:
        @ We consume 32-bytes per iteration, so prefetch 4 iterations ahead.
        @ TODO: experiment with different prefetch lengths on different
        @ architectures.
        pld               [r0, #128]    @ Prefetch position data

        @ {q0, q1} == xPosition and yPosition
        @ Four values in each. q0 = (x0, x1, x2, x3)
        vld2.f32          {q0, q1}, [r0]! @ Read in positions; increment ptr

        @ Calculate tags four at a time.
        vmul.f32          q0, q0, q12   @ q0 = x = xPosition * inverseDiameter
        vmul.f32          q1, q1, q12   @ q1 = y = yPosition * inverseDiameter
        vmul.f32          q0, q0, q13   @ q0 = x * xScale
        vadd.f32          q1, q1, q15   @ q1 = y + yOffset
        vadd.f32          q0, q0, q14   @ q0 = x * xScale + xOffset
        vcvt.u32.f32      q1, q1        @ q1 = (uint32)(y + yOffset)
        vcvt.u32.f32      q0, q0        @ q0 = (uint32)(x * xScale + xOffset)
        vsli.u32          q0, q1, #20   @ q0 = tag
                                        @    = ((uint32)(y + yOffset) <<yShift)
                                        @    + (uint32)(xScale * x + xOffset)

        @ Decrement loop counter; sets the 'gt' flag used in 'bgt' below.
        @ Pipelining is best if there are instructions between the 'subs' and
        @ 'bgt' instructions, since it takes a few cycles for the result of
        @ 'subs' to propegate to the flags register.
        subs              r1, r1, #4

        @ Write out, ignoring index.
        pld               [r3, #64]     @ Prefetch output tag array
        vst1.f32          {q0}, [r3]!   @ write out tags; increment ptr

        bgt               .L_CalculateTags_MainLoop

.L_CalculateTags_Return:
        bx                lr



        .balign   4
        .thumb_func
        @
        @ Once four contacts have been found, calculate their weights and
        @ normals (using SIMD, so all at once).
        @
        @ Also, grab their flags from the flags buffer, and OR them together.
        @ This flag grabbing is slow because we access the flag buffer in a
        @ random order. We use prefetch instructions 'pld' to minimize the
        @ cost of cache misses.
        @
FindContacts_PostProcess:
        @ Preload first four flag addresses into cache.
        @ Note: hardware only has four preload slots.
        ldrh              r9, [r4]
        ldrh              r10, [r4, #2]
        ldrh              r11, [r4, #16]
        ldrh              r12, [r4, #18]
        pld               [r7, r9, lsl #2]
        pld               [r7, r10, lsl #2]
        pld               [r7, r11, lsl #2]
        pld               [r7, r12, lsl #2]

        @ q0 = packedIndices -- indices output to b2ParticleContact
        @ q1 = distBtParticlesSq -- will be used to calculate weight
        @ q2 = diffX -- will be used to calculate normal
        @ q3 = diffY -- will be used to calculate normal
        add               r8, r4, #32
        vld4.f32          {d0, d2, d4, d6}, [r4]
        vld4.f32          {d1, d3, d5, d7}, [r8]

        @ Use distSq to estimate 1 / dist.
        vrsqrte.f32       q8, q1      @ q8 = 1 / dist -- (rough estimate)
        vmul.f32          q9, q8, q1  @ q9 = 1 / dist * distSq -- (appr 'dist')
        vrsqrts.f32       q9, q9, q8  @ q9 = (3 - 1/dist * dist) / 2 -- (error)
        vmul.f32          q8, q8, q9  @ q8 = (error) / dist -- (estimate)
        vcgt.f32          q9, q8, #0  @ q8 = 1 / dist > 0 (true if not NaN)
        vand              q8, q8, q9  @ q8 = 1 / dist if valid, or 0 if NaN

        @ Since we expand the output to include 'weight', we need to preserve
        @ subsequent contacts. Note that there may be up to 7 contacts waiting
        @ to be post-processed, since we output contacts in up-to groups of 4.
        add               r8, r4, #64
        vldmia            r8, {q9, q10, q11}

        @ Load first four flags, 'or' them in pairs, then write to destination.
        ldr               r9, [r7, r9, lsl #2]
        ldr               r10, [r7, r10, lsl #2]
        ldr               r11, [r7, r11, lsl #2]
        ldr               r12, [r7, r12, lsl #2]
        orr               r9, r9, r10
        orr               r11, r11, r12
        str               r9, [r4, #16]
        str               r11, [r4, #36]

        @ Preload the next four flags into cache.
        ldrh              r9, [r4, #32]
        ldrh              r10, [r4, #34]
        ldrh              r11, [r4, #48]
        ldrh              r12, [r4, #50]
        pld               [r7, r9, lsl #2]
        pld               [r7, r10, lsl #2]
        pld               [r7, r11, lsl #2]
        pld               [r7, r12, lsl #2]

        @ Calculate normal and weight.
        vmul.f32          q1, q1, q8     @ q1 = distSq / dist = dist
        vmul.f32          q2, q2, q8     @ q2 = normX = diffX / dist
        vmul.f32          q1, q1, q14    @ q1 = dist / diameter
        vmul.f32          q3, q3, q8     @ q3 = normY = diffY / dist
        vsub.f32          q1, q12, q1    @ q1 = weight = 1 - dist / diameter

        @ Store again, making room for 'weight' member variable this time.
        @ TODO OPT: Interleave with 'or' instructions below.
        mov               r8, #20        @ r8 = 20 = sizeof(b2ParticleContact)
        vst4.f32          {d0[0], d2[0], d4[0], d6[0]}, [r4], r8
        vst4.f32          {d0[1], d2[1], d4[1], d6[1]}, [r4], r8
        vst4.f32          {d1[0], d3[0], d5[0], d7[0]}, [r4], r8
        vst4.f32          {d1[1], d3[1], d5[1], d7[1]}, [r4], r8
        mov               r8, #12        @ r8 = 12 = sizeof(FindContactInput)

        @ Load next four flags, 'or' them in pairs, then write to destination.
        ldr               r9, [r7, r9, lsl #2]
        ldr               r10, [r7, r10, lsl #2]
        ldr               r11, [r7, r11, lsl #2]
        ldr               r12, [r7, r12, lsl #2]
        orr               r9, r9, r10
        orr               r11, r11, r12
        str               r9, [r4, #-24]
        str               r11, [r4, #-4]

        @ Update output pointers. Since we output 4 contacts, and added 4 bytes
        @ for 'weight' on each contact, the output pointer must be advanced by
        @ 16 bytes.
        add               r3, r3, #16
        add               r5, r5, #4          @ numContacts += 4

        @ Restore subsequent contacts. That is, contacts that have yet to be
        @ post-processed.
        vstmia            r4, {q9, q10, q11}

        bx                lr


        @ When used with the 'vtbl' instruction, grabs the first byte of every
        @ word, and places it in the first word. Fills the second word with 0s.
        @ For example, (0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF)
        @              ==> (0xFF0000FF, 0x00000000)
CONST_IS_CLOSE_TABLE_INDICES:
        .byte           0
        .byte           4
        .byte           8
        .byte           12
        .byte           0xFF
        .byte           0xFF
        .byte           0xFF
        .byte           0xFF


        .balign   4
        .global   FindContactsFromChecks_Simd
        .thumb_func
FindContactsFromChecks_Simd:
        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        @
        @ void FindContactsFromChecks_Simd(
        @       const FindContactInput* reordered,
        @       const FindContactCheck* checks,
        @       int numChecks,
        @       const float& particleDiameterSq,
        @       const float& particleDiameterInv,
        @       const uint32* flags,
        @       b2GrowableBuffer<b2ParticleContact>& contacts)
        @
        @    Parameters
        @  r0: *reordered
        @  r1: *checks
        @  r2: numChecks
        @  r3: particleDiameterSq
        @  [sp]: particleDiameterInv
        @  [sp+4]: *flags
        @  [sp+8]: contacts
        @
        @    Persistent Variables
        @  r0: *reordered (constant)
        @  r1: *checks (advance once per iteration)
        @  r2: numChecks (decrement once per iteration)
        @  r3: *out <-- next free entry of outContacts array
        @  r4: *postProcess <-- entry on-deck to be post-processed
        @  r5: numContacts
        @  r6: maxSafeContacts
        @  r7: *flags (constant)
        @  r8: 20 = sizeof(b2ParticleContact), or
        @      12 = sizeof(FindContactInput) (constants)
        @
        @    Scratch Variables
        @  r9:
        @  r10: address of current particle position
        @  r11: address of comparator particle positions
        @  r12: isClose (compacted)
        @
        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        @
        @    Scratch
        @  q0 == index     ------> packedIndices
        @  q1 == positionX ---_    distBtParticlesSq
        @  q2 == positionY --_ --> normX
        @  q3 ==              ---> normY
        @
        @    Unused (note: these are callee-saved)
        @  q4 ==
        @  q5 ==
        @  q6 ==
        @  q7 ==
        @
        @    Scratch
        @  q8 == comparatorIndices
        @  q9 == comparatorPositionX
        @ q10 == comparatorPositionY
        @ q11 ==
        @
        @    Constants
        @ q12 == 1.0f
        @ q13 == isClose table indices
        @ q14 == 1 / particleDiameter
        @ q15 == particleDiameterSq

        push              {r4-r11, lr}

        @ Load constants from registers and stack.
        vld1.f32          {d30[],d31[]}, [r3]  @ q15 = particleDiameterSq
        ldr               r12, [sp, #36]       @ r12 = particleDiameterInv
        vld1.f32          {d28[],d29[]}, [r12] @ q14 = particleDiameterInv
        ldr               r9, [sp, #44]    @ r9 = contacts
        ldr               r7, [sp, #40]    @ r7 = flags
        ldr               r3, [r9, #0]     @ r3 = out = contacts.data
        ldr               r6, [r9, #8]     @ r6 = contacts.capacity
        mov               r4, r3           @ r4 = postProcess = outContacts
        mov               r5, #0           @ r5 = numContacts
        sub               r6, r6, #8       @ r6 = maxSafeContacts = capacity - 8
        mov               r8, #12          @ r8 = 12 = sizeof(FindContactInput)

        @ Perform zero iterations if 'numChecks' is empty.
        @ Must happen after initializing r5 = numContacts = 0.
        cmp               r2, #0
        ble               .L_FindContacts_Return

        @ Load and calculate remaining constants.
        vmov.f32          q12, #1.0        @ q12 = 1.0f splatted
        adr               r12, CONST_IS_CLOSE_TABLE_INDICES
        vld1.8            {d26}, [r12]     @ q13 = *CONST_IS_CLOSE_TABLE_INDICES

.L_FindContacts_MainLoop:
        pld               [r1, #8]         @ prefetch two loops ahead

        @ r10 <== Address of 'position', the current particle position
        @ r11 <== Address of '&comparator[0]', the first particle position we
        @         compare against.
        ldr               r10, [r1], #4    @ r10 = positionIndex|comparatorIndex
        smlatb            r11, r10, r8, r0 @ r11 = address of first comparator
        smlabb            r10, r10, r8, r0 @ r10 = address of current input
        add               r12, r11, #24    @ r12 = address of third comparator

        @ Exit if not enough space in output array (part 1)
        cmp               r5, r6

        @ {q0, q1, q2} == index, positionX, positionY, splatted across vector
        vld3.f32          {d0[], d2[], d4[]}, [r10]
        vld3.f32          {d1[], d3[], d5[]}, [r10]

        @ {q8, q9, q10} == comparatorIndices, comparatorPosX and comparatorPosY
        @                 positions we compare against (positionX, positionY)
        vld3.f32          {d16, d18, d20}, [r11]
        vld3.f32          {d17, d19, d21}, [r12]

        @ q0 = packedIndices -- indices output to b2ParticleContact
        @ q1 = distBtParticlesSq -- will be used to calculate weight
        @ q2 = diffX -- will be used to calculate normal
        @ q3 = diffY -- will be used to calculate normal
        vsub.f32          q3, q10, q2  @ q3 = diffY = comparatorPosY - positionY
        vsub.f32          q2, q9, q1   @ q2 = diffX = comparatorPosX - positionX
        vsli.32           q0, q8, #16  @ q0 = comparatorIndex[i] << 16 | index
        vmul.f32          q1, q3, q3   @ q1 = diffX * diffX
        vmla.f32          q1, q2, q2   @ q1 = diffX * diffX + diffY * diffY

        @ Determine if each particle is close enough to output.
        @ Pack the isClose bitmap (four T or F) into a 32-bit bitmap.
        @ Move 32-bit bitmap to CPU register, for conditional operations.
        @ Note: NEON to CPU register moves are slow (20 cyclds) on some
        @ implementations of NEON.
        @   isClose = distBtParticlesSq < particleDiameterSq
        vclt.f32          q8, q1, q15          @ q8 == isClose
        vtbl.8            d16, {d16,d17}, d26  @ q8[0] == isClose(packed)
        vmov.32           r12, d16[0]          @ q8[0] ==> r12.

        @ If not enough space in output array, grow it.
        @ This is a heavy operation, but should happen rarely.
        ble               .L_FindContacts_Output
        ldr               r9, [sp, #44]        @ r9 = contacts
        str               r5, [r9, #4]         @ contacts.count = numContacts
        ldr               r10, [r9, #0]        @ r10 = contacts.data
        push              {r0-r3, r9, r10, r12}
        vpush             {q0, q1, q2, q3}
        vpush             {q12, q13, q14, q15}
        mov               r0, r9               @ r0 = contacts
        bl                GrowParticleContactBuffer
        vpop              {q12, q13, q14, q15}
        vpop              {q0, q1, q2, q3}
        pop               {r0-r3, r9, r10, r12}

        @ The output array was reallocated, so update 'out', 'postProcess' and
        @ 'maxSafeContacts' pointers.
        ldr               r6, [r9, #8]         @ r6 = contacts.capacity
        ldr               r9, [r9, #0]         @ r9 = contacts.data
        sub               r9, r9, r10          @ r9 = data buffer offset
        sub               r6, r6, #8           @ r6 = maxSafeContacts
        add               r3, r3, r9           @ r3 += data buffer offset
        add               r4, r4, r9           @ r4 += data buffer offset

.L_FindContacts_Output:
        @ Store results to memory, but only results that are close
        tst               r12, 0xFF
        it                ne
        vst4ne.32         {d0[0],d2[0],d4[0],d6[0]}, [r3]! @ Store 1st contact

        tst               r12, 0xFF00
        it                ne
        vst4ne.32         {d0[1],d2[1],d4[1],d6[1]}, [r3]! @ Store 2nd contact

        tst               r12, 0xFF0000
        it                ne
        vst4ne.32         {d1[0],d3[0],d5[0],d7[0]}, [r3]! @ Store 3rd contact

        tst               r12, 0xFF000000
        it                ne
        vst4ne.32         {d1[1],d3[1],d5[1],d7[1]}, [r3]! @ Store 4th contact

        @ post-process the last four elements that have been output
        @ r12 = 5th element to not be post-processed yet
        add               r12, r4, #64         @ r12 = nextPostProcess
        cmp               r3, r12
        it                ge
        blge              FindContacts_PostProcess

        @ decrement loop counter; sets the 'gt' flag used in 'bgt' below
        subs              r2, r2, #1
        bgt               .L_FindContacts_MainLoop

.L_FindContacts_PostProcessRemainingItems:
        @ If at least one output item needs post-processing, do it.
        subs              r12, r3, r4
        ble               .L_FindContacts_Return

        @ r12/16 = num extra contacts to process
        add               r5, r5, r12, lsr #4  @ numContacts += num extra
        push              {r5}                 @ Save numContacts, since stomped

        @ Ensure indices past end of array are zeroed out.
        @ We process 4 contacts in FindContacts_PostProcess, even if we only
        @ have one left to process.
        mov               r12, #0
        str               r12, [r3]
        str               r12, [r3, #16]
        str               r12, [r3, #32]

        bl                FindContacts_PostProcess
        pop               {r5}                 @ Restore numContacts

.L_FindContacts_Return:
        @ Set the final number of contacts in the output buffer.
        ldr               r9, [sp, #44]        @ r9 = contacts
        str               r5, [r9, #4]         @ contacts.count = numContacts

        @ Return by popping the original lr into pc.
        pop               {r4-r11, pc}