Skip to content

Commit

Permalink
Add batched angle c-function, fix memleak in batched benchmark. Fixes #…
Browse files Browse the repository at this point in the history
  • Loading branch information
viblo committed Oct 20, 2023
1 parent 1d85fed commit c10fd95
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 57 deletions.
21 changes: 16 additions & 5 deletions benchmarks/batched-position-api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ def non_batched():
positions.append(b.position.y)


position_arr = pymunk.cp.cpVectArrayNew(0)
angle_arr = pymunk.cp.cpFloatArrayNew(0)


def batched():
arr = pymunk.ffi.new("cpVectArr *", {"num": 0})
arr.num = 0
arr.max = 0
pymunk.cp.cpSpaceGetBodyPositions(s._space, arr)
buf = pymunk.ffi.buffer(arr.arr, pymunk.ffi.sizeof("cpVect") * arr.num)
position_arr.num = 0
angle_arr.num = 0
pymunk.cp.cpSpaceGetBodyPositions(s._space, position_arr, angle_arr)
buf = pymunk.ffi.buffer(
position_arr.arr, pymunk.ffi.sizeof("cpVect") * position_arr.num
)
mv = memoryview(buf)
positions = list(mv.cast("d"))
buf = pymunk.ffi.buffer(angle_arr.arr, pymunk.ffi.sizeof("cpFloat") * angle_arr.num)
mv = memoryview(buf)
angles = list(mv.cast("d"))


if __name__ == "__main__":
Expand Down Expand Up @@ -64,3 +72,6 @@ def batched():
)
)
)

pymunk.cp.cpVectArrayFree(position_arr)
pymunk.cp.cpFloatArrayFree(angle_arr)
10 changes: 10 additions & 0 deletions pymunk_cffi/chipmunk_cdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ struct cpArbiter
enum cpArbiterState state;
};

struct cpArray
{
int num, max;
void **arr;
};

///////////////////////////////////////////
// cpVect.h
///////////////////////////////////////////
Expand Down Expand Up @@ -1516,3 +1522,7 @@ typedef void (*cpHashSetIteratorFunc)(void *elt, void *data);
void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data);

cpArbiter *cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b);

cpArray *cpArrayNew(int size);
void cpArrayPush(cpArray *arr, void *object);
void cpArrayFree(cpArray *arr);
173 changes: 126 additions & 47 deletions pymunk_cffi/extensions.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,89 +4,166 @@
// Functions to support efficient batch API
//

typedef struct cpVectArr cpVectArr;
typedef struct cpVectArray cpVectArray;
typedef struct cpFloatArray cpFloatArray;

struct cpVectArray
{
int num, max;
cpVect *arr;
};

struct cpVectArr {
int num, max;
cpVect *arr;
struct cpFloatArray
{
int num, max;
cpFloat *arr;
};

void cpSpaceBodyIteratorFuncForPositions(cpBody *body, void *data){
cpVectArr *arr = (cpVectArr*)data;
cpVectArray *
cpVectArrayNew(int size)
{
cpVectArray *arr = (cpVectArray *)cpcalloc(1, sizeof(cpVectArray));

arr->num = 0;
arr->max = (size ? size : 4);
arr->arr = (void **)cpcalloc(arr->max, sizeof(void *));

return arr;
}

void cpVectArrayFree(cpVectArray *arr)
{
if (arr)
{
cpfree(arr->arr);
arr->arr = NULL;

cpfree(arr);
}
}

cpFloatArray *
cpFloatArrayNew(int size)
{
cpFloatArray *arr = (cpFloatArray *)cpcalloc(1, sizeof(cpFloatArray));

arr->num = 0;
arr->max = (size ? size : 4);
arr->arr = (void **)cpcalloc(arr->max, sizeof(void *));

return arr;
}

void cpFloatArrayFree(cpFloatArray *arr)
{
if (arr)
{
cpfree(arr->arr);
arr->arr = NULL;

cpfree(arr);
}
}

void cpSpaceBodyIteratorFuncForPositions(cpBody *body, void *data)
{
cpVectArray *arr = (cpVectArray *)data;
cpVect v = cpBodyGetPosition(body);
if (arr->num == (arr->max - 1) || arr->num == arr->max) {
arr->max = 3*(arr->max + 1)/2;
arr->arr = (cpVect*)cprealloc(arr->arr, arr->max*sizeof(cpVect));
if (arr->num == (arr->max - 1) || arr->num == arr->max)
{
arr->max = 3 * (arr->max + 1) / 2;
arr->arr = (cpVect *)cprealloc(arr->arr, arr->max * sizeof(cpVect));
}
arr->arr[arr->num] = v;
arr->num++;

}

void cpSpaceGetBodyPositions(cpSpace *space, cpVectArr *arr) {
cpSpaceEachBody(space, cpSpaceBodyIteratorFuncForPositions, arr);

void cpSpaceBodyIteratorFuncForAngles(cpBody *body, void *data)
{
cpFloatArray *arr = (cpFloatArray *)data;
cpFloat v = cpBodyGetAngle(body);
if (arr->num == (arr->max - 1) || arr->num == arr->max)
{
arr->max = 3 * (arr->max + 1) / 2;
arr->arr = (cpVect *)cprealloc(arr->arr, arr->max * sizeof(cpFloat));
}
arr->arr[arr->num] = v;
arr->num++;
}

void cpSpaceGetBodyPositions(cpSpace *space, cpVectArray *positionArr, cpFloatArray *angleArr)
{
// TODO: Room for optimizations here..
cpSpaceEachBody(space, cpSpaceBodyIteratorFuncForPositions, positionArr);
cpSpaceEachBody(space, cpSpaceBodyIteratorFuncForAngles, angleArr);
}

//
// Functions to support pickle of arbiters the space has cached
//
typedef struct cpContact cpContact;

cpHashValue cpShapeGetHashID(cpShape *shape){
cpHashValue cpShapeGetHashID(cpShape *shape)
{
return shape->hashid;
}
void cpShapeSetHashID(cpShape *shape, cpHashValue id){
void cpShapeSetHashID(cpShape *shape, cpHashValue id)
{
shape->hashid = id;
}


cpHashValue cpSpaceGetShapeIDCounter(cpSpace *space){
cpHashValue cpSpaceGetShapeIDCounter(cpSpace *space)
{
return space->shapeIDCounter;
}
void cpSpaceSetShapeIDCounter(cpSpace *space, cpHashValue counter){
void cpSpaceSetShapeIDCounter(cpSpace *space, cpHashValue counter)
{
space->shapeIDCounter = counter;
}

cpTimestamp cpSpaceGetTimestamp(cpSpace *space) {
cpTimestamp cpSpaceGetTimestamp(cpSpace *space)
{
return space->stamp;
}

void cpSpaceSetTimestamp(cpSpace *space, cpTimestamp stamp) {
void cpSpaceSetTimestamp(cpSpace *space, cpTimestamp stamp)
{
space->stamp = stamp;
}

void cpSpaceSetCurrentTimeStep(cpSpace *space, cpFloat curr_dt) {
space->curr_dt = curr_dt;
void cpSpaceSetCurrentTimeStep(cpSpace *space, cpFloat curr_dt)
{
space->curr_dt = curr_dt;
}

typedef void (*cpArbiterIteratorFunc)(cpArbiter *arb, void *data);

void cpSpaceEachCachedArbiter(cpSpace *space, cpArbiterIteratorFunc func, void *data) {
cpHashSetEach(space->cachedArbiters, (cpHashSetIteratorFunc) func, data);
void cpSpaceEachCachedArbiter(cpSpace *space, cpArbiterIteratorFunc func, void *data)
{
cpHashSetEach(space->cachedArbiters, (cpHashSetIteratorFunc)func, data);
}

static inline cpCollisionHandler *
cpSpaceLookupHandler(cpSpace *space, cpCollisionType a, cpCollisionType b, cpCollisionHandler *defaultValue)
{
cpCollisionType types[] = {a, b};
cpCollisionHandler *handler = (cpCollisionHandler *)cpHashSetFind(space->collisionHandlers, CP_HASH_PAIR(a, b), types);
return (handler ? handler : defaultValue);
cpCollisionType types[] = {a, b};
cpCollisionHandler *handler = (cpCollisionHandler *)cpHashSetFind(space->collisionHandlers, CP_HASH_PAIR(a, b), types);
return (handler ? handler : defaultValue);
}

void cpSpaceAddCachedArbiter(cpSpace *space, cpArbiter *arb) {
void cpSpaceAddCachedArbiter(cpSpace *space, cpArbiter *arb)
{
// Need to init the contact buffer
cpSpacePushFreshContactBuffer(space);

int numContacts = arb->count;
struct cpContact *contacts = arb->contacts;
// Restore contact values back to the space's contact buffer memory
arb->contacts = cpContactBufferGetArray(space);
memcpy(arb->contacts, contacts, numContacts*sizeof(struct cpContact));

memcpy(arb->contacts, contacts, numContacts * sizeof(struct cpContact));
cpSpacePushContacts(space, numContacts);

// Reinsert the arbiter into the arbiter cache
const cpShape *a = arb->a, *b = arb->b;
const cpShape *shape_pair[] = {a, b};
Expand All @@ -95,30 +172,32 @@ void cpSpaceAddCachedArbiter(cpSpace *space, cpArbiter *arb) {

// Set handlers to their defaults
cpCollisionType typeA = a->type, typeB = b->type;
cpCollisionHandler *defaultHandler = &space->defaultHandler;
cpCollisionHandler *handler = arb->handler = cpSpaceLookupHandler(space, typeA, typeB, defaultHandler);

// Check if the types match, but don't swap for a default handler which use the wildcard for type A.
cpBool swapped = arb->swapped = (typeA != handler->typeA && handler->typeA != CP_WILDCARD_COLLISION_TYPE);

if(handler != defaultHandler || space->usesWildcards){
// The order of the main handler swaps the wildcard handlers too. Uffda.
arb->handlerA = cpSpaceLookupHandler(space, (swapped ? typeB : typeA), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);
arb->handlerB = cpSpaceLookupHandler(space, (swapped ? typeA : typeB), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);
}
cpCollisionHandler *defaultHandler = &space->defaultHandler;
cpCollisionHandler *handler = arb->handler = cpSpaceLookupHandler(space, typeA, typeB, defaultHandler);

// Check if the types match, but don't swap for a default handler which use the wildcard for type A.
cpBool swapped = arb->swapped = (typeA != handler->typeA && handler->typeA != CP_WILDCARD_COLLISION_TYPE);

if (handler != defaultHandler || space->usesWildcards)
{
// The order of the main handler swaps the wildcard handlers too. Uffda.
arb->handlerA = cpSpaceLookupHandler(space, (swapped ? typeB : typeA), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);
arb->handlerB = cpSpaceLookupHandler(space, (swapped ? typeA : typeB), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);
}

// Update the arbiter's state
cpArrayPush(space->arbiters, arb);

cpfree(contacts);
}

cpArbiter *cpArbiterNew(cpShape *a, cpShape *b) {
cpArbiter *cpArbiterNew(cpShape *a, cpShape *b)
{
cpArbiter *arb = (cpArbiter *)cpcalloc(1, sizeof(struct cpArbiter));
return cpArbiterInit(arb, a, b);
}


cpContact *cpContactArrAlloc(int count){
cpContact *cpContactArrAlloc(int count)
{
return (cpContact *)cpcalloc(count, sizeof(struct cpContact));
}
26 changes: 21 additions & 5 deletions pymunk_cffi/extensions_cdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@
// Functions to support efficient batch API
//

typedef struct cpVectArr cpVectArr;
struct cpVectArr {
typedef struct cpVectArray cpVectArray;
typedef struct cpFloatArray cpFloatArray;

struct cpVectArray
{
int num, max;
cpVect *arr;
};
void cpSpaceGetBodyPositions(cpSpace *space, cpVectArr *arr);

struct cpFloatArray
{
int num, max;
cpFloat *arr;
};

cpVectArray *cpVectArrayNew(int size);
cpFloatArray *cpFloatArrayNew(int size);
void cpVectArrayFree(cpVectArray *arr);
void cpFloatArrayFree(cpFloatArray *arr);

void cpSpaceGetBodyPositions(cpSpace *space, cpVectArray *positionArr, cpFloatArray *angleArr);

//
// Functions to support pickle of arbiters the space has cached
Expand All @@ -26,8 +41,9 @@ void cpSpaceSetCurrentTimeStep(cpSpace *space, cpFloat curr_dt);

typedef void (*cpArbiterIteratorFunc)(cpArbiter *arb, void *data);
void cpSpaceEachCachedArbiter(cpSpace *space, cpArbiterIteratorFunc func, void *data);
extern "Python" {
void ext_cpArbiterIteratorFunc(cpArbiter *arb, void *data);
extern "Python"
{
void ext_cpArbiterIteratorFunc(cpArbiter *arb, void *data);
}
void cpSpaceAddCachedArbiter(cpSpace *space, cpArbiter *arb);

Expand Down

0 comments on commit c10fd95

Please sign in to comment.