kivyfn-3d

35 views
Skip to first unread message

Gibril Hamideh

unread,
Jul 2, 2026, 10:44:03 AM (12 days ago) Jul 2
to Kivy users support

Hi everyone,

I'm currently developing kivyfn-3d, a real-time 3D framework built on top of Kivy. So far I've been able to implement features such as skeletal animation and skinning, custom render passes, shadow mapping (including cubemap shadows), custom textures and framebuffers, and other real-time rendering features using Kivy's existing graphics APIs.

One of the main remaining features I'd love to explore is true GPU instancing. I noticed that Kivy exposes buffer APIs such as glGenBuffers, glBindBuffer, glBufferData, and glVertexAttribPointer, but I couldn't find wrappers for glDrawElementsInstanced, glDrawArraysInstanced, or glVertexAttribDivisor.

Is there a technical reason these functions are not currently exposed? Would the project be open to exposing them in a future release?

For context, here's a small demo of the current state of kivyfn-3d.

cubemap.mp4
ivy.mp4
farm.mp4

ElliotG

unread,
Jul 2, 2026, 11:18:48 AM (12 days ago) Jul 2
to Kivy users support
Good question — this comes down to how Kivy's GL layer is architected, not any deep technical obstacle.

Kivy's OpenGL access goes through a layer called CGL (kivy/graphics/cgl.pyx), which defines a fixed C struct (GLES2_Context) of function pointers that every backend (gl, glew, sdl2, angle_sdl3, mock) has to fill in. Kivy can force OpenGL ES2, which is always used on Android, iOS, Raspberry Pi, and Mali platforms. The struct is deliberately scoped to what GLES 2.0 guarantees, because that's the lowest common denominator Kivy has to run on across desktop and mobile.

glDrawArraysInstanced, glDrawElementsInstanced, and glVertexAttribDivisor are core in GLES 3.0 / GL 3.1+ (or available in GLES2 only via the GL_ANGLE_instanced_arrays / GL_EXT_instanced_arrays extensions). Since they're outside the GLES2 baseline, they were never added to the GLES2_Context struct, and consequently kivy.graphics.opengl — which is a Python wrapper for OpenGL commands, and doesn't wrap every OpenGL command since Kivy uses the C binding for performance and expects you to stick to the Kivy Graphics API when possible — has no wrapper for them either. It's an omission driven by "not part of our minimum target profile," not a deliberate exclusion or a limitation of the underlying platforms themselves — most of Kivy's desktop backends (gl/glew) are sitting on top of a real desktop GL context that supports instancing just fine, but Kivy doesn't expose it because iOS/Android GLES2 devices might not.

How to get instancing anyway

You have a few realistic options, roughly in order of how much you want to touch Kivy internals:

  1. Grab the function pointers yourself at runtime. Since Kivy already has a live GL context by the time your code runs, you can use ctypes to dlopen/dlsym (or SDL_GL_GetProcAddress/eglGetProcAddress via ctypes on platforms where straight dlsym won't find extension entry points) and call glDrawArraysInstanced etc. directly, bypassing CGL entirely. This is the path of least resistance and is what most people in your position do — it doesn't require rebuilding Kivy or patching cgl.

  2. Use PyOpenGL alongside Kivy's context, as some people in the Kivy community have done for custom VBO/FBO work — it can call into the same context Kivy created, though you have to be careful not to disturb Kivy's own GL state tracking. There's precedent for this kind of side-by-side use in the community, though it's a bit more failure-prone with FBOs/state changes than direct ctypes calls.

  3. Extend CGL itself. Since you're already deep in custom render passes and shadow mapping, adding glDrawArraysInstanced, glDrawElementsInstanced, and glVertexAttribDivisor to the GLES2_Context struct and each backend implementation (cgl_backend/cgl_glew.pyx, cgl_sdl2.pyx, etc.) with an IS_GLES_PLATFORM fallback — similar to how fbo.pyx handles GL_DEPTH_STENCIL_ATTACHMENT as a GLES 3 symbol that works and is required on "GLES 2" on iOS, noting that adding it to headers can be complicated because it doesn't exist in gl2.h — would be a legitimate and honestly pretty welcome PR. Given the precedent of handling GLES3-on-GLES2 symbols that way already, gating instancing behind runtime capability checks (or an EXT/ANGLE fallback on true GLES2 devices) would fit the existing pattern.

Given you're building a full 3D framework on Kivy, option 3 might be worth it long-term rather than routing instancing calls around CGL forever.

You might be interested in taking a look at http://kivent.org/ the project is no longer active, but you might find some useful approaches for what you are doing. 

Your demos look great! 

Gibril Hamideh

unread,
Jul 3, 2026, 11:03:17 AM (11 days ago) Jul 3
to Kivy users support

Thanks Elliot, that makes a lot of sense.

I think the path I’ll take for now is to prototype instancing through ctypes first, mainly so I can prove the rendering path, per-instance attributes, capability checks, and fallback behavior inside kivyfn-3d without requiring a custom Kivy build.

Longer term, if the approach works well, I’d definitely be interested in looking at a proper CGL-based implementation and possibly opening a PR to expose glDrawArraysInstanced, glDrawElementsInstanced, and glVertexAttribDivisor behind runtime capability checks / extension fallbacks. That feels like the cleanest final direction, especially if the backend differences can live inside Kivy instead of every higher-level framework having to solve them separately.

For kivyfn-3d, I’ll probably treat instancing as an optional renderer capability: use it when available, fall back to normal rendering when it is not, and allow strict mode for users who explicitly require GPU instancing.

Also thanks for pointing me toward Kivent — I’ll take a look. I’m building kivyfn-3d as part of a broader Kivy-focused toolkit, so it’s really useful to see older projects that pushed Kivy in this direction.

Reply all
Reply to author
Forward
0 new messages