Panda3D on the RaspberryPi [now with rasbian deb]

Hi folks,

exciting news. with a bit of tinkering i got panda3d to run on a raspberry pi.
so far there are quite a number of limitations:
most important one:no propper X11 windowing support, everything is scaled to fullscreen and rendered on top of the desktop.
aside from that it runs rather well.

an experimental deb package for the rasbian wheezy can be found here

for those who want to compile panda on the raspberry, continue reading.

assuming you have your rasperry running already you need to set up cross-compile. there’s a nice tutorial here:

after you chrooted into your raspberry environment, install the dependencies like you would when compiling on the desktop.

copy the panda source over to the sd card. you need to make the following changes:
in /panda/src/egldisplay/eglGraphicsPipe.cxx

#include "bcm_host.h"

to the includes


at the top of the constructor eglGraphicsPipe::eglGraphicsPipe

i messed around in this file so i have no idea what part is original and what is modified… so in /panda/src/egldisplay/eglGraphicsWindow.cxx replace the entire openWindow() function with

//     Function: eglGraphicsWindow::open_window
//       Access: Protected, Virtual
//  Description: Opens the window right now.  Called from the window
//               thread.  Returns true if the window is successfully
//               opened, or false if there was a problem.
bool eglGraphicsWindow::
open_window() {
  eglGraphicsPipe *egl_pipe;
  DCAST_INTO_R(egl_pipe, _pipe, false);

  // GSG Creation/Initialization
  eglGraphicsStateGuardian *eglgsg;
  if (_gsg == 0) {
    // There is no old gsg.  Create a new one.
    eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL);
    eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
    _gsg = eglgsg;
  } else {
    // If the old gsg has the wrong pixel format, create a
    // new one that shares with the old gsg.
    DCAST_INTO_R(eglgsg, _gsg, false);
    if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) {
      eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg);
      eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
      _gsg = eglgsg;

  XVisualInfo *visual_info = eglgsg->_visual;
  if (visual_info == NULL) {
    // No X visual for this fbconfig; how can we open the window?
      << "No X visual: cannot open window.\n";
    return false;
  Visual *visual = visual_info->visual;
  int depth = visual_info->depth;

  if (!_properties.has_origin()) {
    _properties.set_origin(0, 0);
  if (!_properties.has_size()) {
    _properties.set_size(100, 100);
  X11_Window parent_window = egl_pipe->get_root();
  WindowHandle *window_handle = _properties.get_parent_window();
  if (window_handle != NULL) {
      << "Got parent_window " << *window_handle << "\n";
    WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
    if (os_handle != NULL) {
        << "os_handle type " << os_handle->get_type() << "\n";
      if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
        NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
        parent_window = x11_handle->get_handle();
      } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
        NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
        parent_window = (X11_Window)int_handle->get_handle();
  _parent_window_handle = window_handle;


  _event_mask =
    ButtonPressMask | ButtonReleaseMask |
    KeyPressMask | KeyReleaseMask |
    EnterWindowMask | LeaveWindowMask |
    PointerMotionMask |
    FocusChangeMask |

  // Initialize window attributes
  XSetWindowAttributes wa;
  wa.background_pixel = XBlackPixel(_display, _screen);
  wa.border_pixel = 0;
  wa.colormap = _colormap;
  wa.event_mask = _event_mask;

  unsigned long attrib_mask =
    CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

  _xwindow = XCreateWindow
    (_display, parent_window,
     _properties.get_x_origin(), _properties.get_y_origin(),
     _properties.get_x_size(), _properties.get_y_size(),
     0, depth, InputOutput, visual, attrib_mask, &wa);

  if (_xwindow == (X11_Window)0) {
      << "failed to create X window.\n";
    return false;
  set_wm_properties(_properties, false);

  // We don't specify any fancy properties of the XIC.  It would be
  // nicer if we could support fancy IM's that want preedit callbacks,
  // etc., but that can wait until we have an X server that actually
  // supports these to test it on.
  XIM im = egl_pipe->get_im();
  _ic = NULL;
  if (im) {
    _ic = XCreateIC
       XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
    if (_ic == (XIC)NULL) {
        << "Couldn't create input context.\n";

  if (_properties.get_cursor_hidden()) {
    XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
  //try to setup the broadcom way.

  static EGL_DISPMANX_WINDOW_T nativewindow;

   DISPMANX_ELEMENT_HANDLE_T dispman_element;
   DISPMANX_DISPLAY_HANDLE_T dispman_display;
   DISPMANX_UPDATE_HANDLE_T dispman_update;
   VC_RECT_T dst_rect;
   VC_RECT_T src_rect;

   int display_width;
   int display_height;

   // You can hardcode the resolution here:
   display_width = _properties.get_x_size();
   display_height = _properties.get_y_size();

   dst_rect.x = (DISPMANX_TRANSFORM_T) _properties.get_x_origin();
   dst_rect.y = (DISPMANX_TRANSFORM_T) _properties.get_y_origin();
   dst_rect.width = (DISPMANX_TRANSFORM_T) display_width;
   dst_rect.height = (DISPMANX_TRANSFORM_T) display_height;

   src_rect.x = 0;
   src_rect.y = 0;
   src_rect.width = display_width << 16;
   src_rect.height = display_height << 16;

   dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
   dispman_update = vc_dispmanx_update_start( 0 );

   dispman_element = vc_dispmanx_element_add ( dispman_update, 
      dispman_display, 0/*layer*/, &dst_rect, 0/*src*/,
      &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 
      0/*clamp*/, (DISPMANX_TRANSFORM_T)0/*transform*/);

   nativewindow.element = dispman_element;
   nativewindow.width = display_width;
   nativewindow.height = display_height;
   vc_dispmanx_update_submit_sync( dispman_update );

  _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  if (_egl_display == EGL_NO_DISPLAY )
      << "Failed to eglGetDisplay.\n";
    return false;

  _egl_surface = eglCreateWindowSurface(_egl_display,  eglgsg->_fbconfig, 
                         &nativewindow, NULL);
   if ( _egl_surface == EGL_NO_SURFACE )
      << "Failed to eglCreateWindowSurface.\n";
      return false;
    if ( !eglMakeCurrent(_egl_display,_egl_surface,_egl_surface, eglgsg->_context) )
      << "Failed to eglMakeCurrent.\n";
      return false;
  //_egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL);
  //if (eglGetError() != EGL_SUCCESS) {
  //  egldisplay_cat.error()
  //    << "Failed to create window surface.\n";
  //  return false;

  if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) {
    egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
      << get_egl_error_string(eglGetError()) << "\n";
  if (!eglgsg->is_valid()) {
    return false;
  if (!eglgsg->get_fb_properties().verify_hardware_software
      (_fb_properties, eglgsg->get_gl_renderer())) {
    return false;
  _fb_properties = eglgsg->get_fb_properties();

  XMapWindow(_display, _xwindow);

  if (_properties.get_raw_mice()) {
  } else {
    if (egldisplay_cat.is_debug()) {
        << "Raw mice not requested.\n";

  // Create a WindowHandle for ourselves
  _window_handle = NativeWindowHandle::make_x11(_xwindow);

  // And tell our parent window that we're now its child.
  if (_parent_window_handle != (WindowHandle *)NULL) {

  return true;

a few compiler and linker settings need to be exported. (asuming you are using the raspian image, if not you may need to fix the pathes to the bcm_host.h)

export CFLAGS="-DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads" 
export LDFLAGS="-L/opt/vc/lib/ -lbcm_host "

all that is left do do is call makepanda with --no-sse2 options and your desired parameters.

python makepanda/ --no-sse2 --everything --installer

crosscompiling can be slow. on my dualcore it takes 6 to 8 hours for a complete build. so be patient.

1 Like

That’s pretty awesome. :slight_smile:

i just uploaded a new version, this time compiled to use the raspberrys hardware float unit. for me it reduced time spend on a single frame by 2.5ms. not much. but it’s a start … ualhf1.deb

i forgot to mention that you have to set the rendering backend to opengles in your config.prc

great contribution \o/

Nice one Sir, very helpful contribution you made…

The intellect of the wise is like glass; it admits the light of heaven and reflects it.

:smiley: this is great! thank you.

I think we would get quite a few new community members if you publish that on a raspberry pi forum :slight_smile:

Hi there,

Is there any chance to run Panda3d without desktop or directly over X?



I installed panda3d1.8_1.8.0_armhf_actualhf1.deb to my 512MB Raspi running rasbian, and set the load-display to pandagles. I haven’t changed any of the windowing options.

When I try to run the samples (Asteroids and Teapots) or the tutorial program, all I get is a black window.
I tried increasing the video memory to 192MB, but still get a black window.

What have I missed?

That’s really awesome, thanks again

Can anybody post a compiled version for raspberry? The links above are dead. Thanks.