Passing pointers to a Cython function

696 views
Skip to first unread message

Serdar Yegulalp

unread,
Sep 4, 2016, 2:55:33 PM9/4/16
to cython-users
I have a Windows system DLL call that has the following header definition:

GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount);

I'm connecting to the DLL like this:

from ctypes import WinDLL, CDLL, c_long
sharelib = CDLL('opengl32.dll')
ctypedef void (*c_uints2)(unsigned int, int*, int*, int) nogil 
cdef c_uints2 glMultiDrawArrays = (<c_uints2*><size_t>sharelib.wglGetProcAddress('glMultiDrawArrays'))[0]

The function I'm invoking:

glMultiDrawArrays(mode, (c_long * primcount)(*starts), (c_long * primcount)(*sizes), primcount)

What I'm trying to do is figure out how to pass the 2nd and 3rd arguments as pointers according to the header definition. How is this done with a ctypes object like a c_long?

Robert Bradshaw

unread,
Sep 9, 2016, 3:31:28 PM9/9/16
to cython...@googlegroups.com
First off, I think throwing ctypes in there is unnecessarily
complicating things. The standard way to do this would be

cdef extern from "opengl.h": # link to this when building the extension
ctypedef int GLenum
ctypedef int L
ctypedef int GLsizei
void glMultiDrawArrays(GLenum mode, GLint *first, GLsizei *count,
GLsizei primcount)

def draw_stuff(mode, first_py, count_py):
primcount = len(first_py)
cdef GLint* first_c = <GLint*>malloc(sizeof(GLint) * primcount)
cdef GLint* count_c = <GLint*>malloc(sizeof(GLint) * primcount)
try:
for i in range(primcount):
first_c[i] = first_py[i]
count_c[i] = count_py[i]
glMultiDrawArrays(mode, first_c, count_c, primcount)
finally:
free(first_c)
free(count_c)

Of course you may have a more pythonic way of storing your data than a
pair of lists of ints, but that's the general idea. The line xxx_c[i]
= xxx_c[i] will convert the Python object (it could be a ctypes
c_long, or any other convertable-to-int object) to a C int of the
right size.

If you really want to get glMultiDrawArrays from ctypes you could do
the cast that you have above, but without the [0], and then call this
function pointer just the same.

Serdar Yegulalp

unread,
Sep 10, 2016, 2:27:57 PM9/10/16
to cython-users
Thanks much for this; it was a huge help.

I did a little reworking, and I now have the "first" and "count" objects ("starts" and "sizes" in my parlance, as passed from a Python object) provided as array.array types:

cdef unsigned int [:] starts, sizes
cdef int s_len 
starts, sizes = <array.array>self.allocator.starts,<array.array>self.allocator.sizes
s_len = len(starts)
with nogil:
    for z in range(s_len):
        glDrawArrays(mode, starts[z], sizes[z])

Is this ideal, or is there a way to pass the arrays with less overhead? My reading of the Cython docs indicates that this syntax creates a memory view when starts and sizes are allocated before the loop, but everything after that should be fast.

Robert Bradshaw

unread,
Sep 11, 2016, 1:52:18 AM9/11/16
to cython...@googlegroups.com
That is correct.

And, FWIW, fast is relative, the creation of a memory view isn't that
slow, it's just slow compared to, say, adding floating point numbers.
Reply all
Reply to author
Forward
0 new messages