Problems when trying to draw to a P3D PGraphics when called from a thread?

I’m working on a live visualizer program for a band, which is structured like this:

The main Draw() loop creates a thread, wherein whichever animation function is currently running is updated. These animation functions often use one or more PGraphics objects, which are later drawn to the main screen.

Everything was working great for the first version, which was all 2D. Now, I’m working on converting everything over to P3D to get a little fancier.

However, using this exact same setup except with P3D PGraphics objects, the program crashes with the following error:

“You are trying to draw outside OpenGL’s animation thread. Place all drawing commands in the draw() function, or inside your own function as long as they are called from draw(), but not in any event handling functions such as keyPressed() or mousePressed()”

I haven’t changed anything except making the sketch P3D, and making the PGraphics objects P3D. Does anyone out there have any experience with this? If it’s possible, I’d love to keep the current thread() setup for this application rather than restructure the whole thing.

Thanks

I’ve tended to avoid draw threading in the past, because Processing isn’t really set up for it – but based on what I have heard I am surprised that your first (JAVA2D?) version ever worked at all. Under normal circumstances I thought that you shouldn’t be able to draw from a thread without Bad Things Happening. Here is the general advice for making thread-based operations draw-free – essentially, signal the main animation thread that content is ready for drawing:

You cannot draw to the screen from a function called by thread() . Because it runs independently, the code will not be synchronized to the animation thread, causing strange or at least inconsistent results. Use thread() to load files or do other tasks that take time. When the task is finished, set a variable that indicates the task is complete, and check that from inside your draw() method.
thread() / Reference / Processing.org

Thanks for the reply! The quote you’ve added here is basically what I’m doing: the thread() methods draw to a PGraphics object and then flag a boolean. Then, in draw(), if the bool is True, I use image() to draw the PGraphics to the main screen – so nothing is actually drawn to the main window in the treads themselves. This works fine when the PGraphics is initialized with the standard 2D renderer – it’s only when the PGraphics is set to P3D that I get that error.

Although it’s pretty much a hack, here’s the workaround I’ve come up w/: :crazy_face:

/**
 * Threaded PGraphics II (v1.0.1)
 * GoToLoop (2018/Dec/07)
 *
 * https://Discourse.Processing.org/t/
 * problems-when-trying-to-draw-to-a-p3d-pgraphics-
 * when-called-from-a-thread/6301/4
 *
 * https://Forum.Processing.org/two/discussion/25799/
 * how-to-draw-a-rect-in-a-thread#Item_2
 */

static final int W = 250, H = 200, FPS = 10;
Layer layer;

void settings() {
  size(W, H, JAVA2D);
  smooth(3);
}

void setup() {
  frameRate(FPS);
  colorMode(RGB);
  imageMode(CORNER);

  final String[] switches = { ARGS_SKETCH_FOLDER + sketchPath(), "" };
  runSketch(switches, layer = new Layer());

  while (layer.img == null)  delay(1);
}

void draw() {
  getSurface().setTitle("Frames: " + frameCount);
  background((color) random(#000000));
  image(layer.img, 0, 0);
}

public static class Layer extends PApplet {
  static final short INTERVAL = 1*1000, TXT_SIZE = 40;
  static final float BOLD = 2.5;
  static final color BORDER = 0, TXT_COLOR = -1;

  PGraphics pg;
  PImage img;

  void settings() {
    size(1, 1, P2D);
  }

  void setup() {
    getSurface().setVisible(false);
    initDisplay();
  }

  void draw() {
    displayLoop();
  }

  void displayLoop() {
    final PGraphics graph = pg;
    final int cx = pg.width>>1, cy = pg.height>>1;

    for (int frames = 1;; delay(INTERVAL), ++frames) {
      graph.beginDraw();

      graph.clear();
      graph.fill((color) random(#000000));
      graph.rect(cx, cy, cx, cy);

      graph.fill(TXT_COLOR);
      graph.text(frames, cx, cy);

      graph.endDraw();

      transferDisplay();
    }
  }

  void transferDisplay() {
    pg.loadPixels();
    arrayCopy(pg.pixels, img.pixels);
    img.updatePixels();
  }

  void initDisplay() {
    pg = createGraphics(W, H, P2D);
    pg.smooth(8);

    pg.beginDraw();

    pg.colorMode(RGB);
    pg.rectMode(CENTER);

    pg.strokeWeight(BOLD);
    pg.stroke(BORDER);

    pg.textSize(TXT_SIZE);
    pg.textAlign(CENTER, CENTER);

    pg.endDraw();

    img = pg.get();
  }
}

Your design cannot possibly work with P2D or P3D as OpenGL is single threaded - you can only use the GL context from the main animation thread.

It’s not exactly a great idea to do this with the Java2D renderer either, even if it works without being flaky.

You don’t need threads to do what you want to do!

1 Like