Can there exist multiple pairs of beginDraw() and endDraw() invoked on the same PGraphics in the same frame?

For example, would the following be legal and produce valid deterministic results:

var A = createGraphics(...);
var B = createGraphics(...);
… // other stuff
void draw() {
A.beginDraw();
... // draw shapes and sprites to A, normal rendering operations, etc.
A.endDraw();
... // advance various intra-frame logic
A.beginDraw();
... // render the contents of other PGraphics instances (not B) to A
A.endDraw();
...
B.beginDraw();
B.image(A...); // render the contents of A to B
B.endDraw();
...
image(B...) // render the contents of B to the main window
}

Expectation
I would expect (or perhaps “hope” is more appropriate here) that this would result in my screen containing an image of B which itself contains an image of A which itself contains images of other PGraphics objects not listed here overtop of various shapes and sprites that were “initially” rendered to A before the images. The status of this closed github issue (I’m too new to the forums to include every link so the issue route is: processing/processing4/issues/641) led me to believe that this might be the case but it is still not totally clear to me.

Context
I know that the ideal solution would be to batch everything within a single begin/end draw sequence, however, this requires significant refactoring of my “controlled access resource” system I’ve implemented to provide canvases for rendering throughout the application life span (basically, calling “get(id)” to retrieve a canvas automatically calls “open()” on the controlled access resource wrapping the PGraphics object which, in turn, calls beginDraw() and then, when put inside of a try-with-resources block, “close()” is called automatically which, in turn, calls endDraw()) so I’d like to know if it is an explicit requirement before committing to the refactor. These “extra” begin/end draw sequences would be quite infrequent so I do not anticipate much performance impact if they are allowed.

Code

Note

  • This project is using Processing 4.3 as a locally-managed Java library on JDK 26
  • My current logic appears to render the results I expect when there are only 2 PGraphics objects of concern (the main window context and 1 offscreen context which gets scaled and rendered to the window context at the window’s full dimensions) but since my “controlled access resource” wrapper differentiates between the main window PGraphics and child PGraphics objects it is not an accurate representation of the issue I’m asking about (the main PGraphics wrapper essentially overrides the default open/close behavior with no-op implementations since all of this happens within the main PApplet::draw and, thus, we don’t need to signal begin/end of the draw in that case).

Can there exist multiple pairs of beginDraw() and endDraw() invoked on the same PGraphics in the same frame?

Yes

Example code that worked with P2D:

Code
PGraphics a;
PGraphics b;

char c;

void setup()
  {
  size(600, 200, P2D);
  a = createGraphics(180, 180, P2D);
  b = createGraphics(180, 180, P2D);
  
  a.beginDraw();
  //a.background(255, 0, 0); // Set initial background
  a.textAlign(CENTER, CENTER);
  a.textSize(96);
  c = 'A';
  a.endDraw();
  
  b.beginDraw();
  //b.background(0, 255, 0); // Set initial background
  b.endDraw();
  
  imageMode(CENTER);
  
  noLoop();
  }
  
void draw()
  {
  // Set background and paint over
  a.beginDraw();
  a.background(155, 128, 0); // Set background to pain over
  //a.clear();
  a.text(c, a.width/3, a.height/2);
  a.endDraw();
  
  image(a, width/6, height/2); 
  
  c++;
  // Adds to above
  a.beginDraw();
  a.text(c, 2*a.width/3, a.width/2);
  a.endDraw();
  
  c++;
  if(c>'A' + 25) c = 'A';
  
  image(a, 3*width/6, height/2);  
  
  // Display image
  b.beginDraw();
  b.image(a, 0, 0);
  b.endDraw();
  
  image(b, 5*width/6, height/2);
  
  noLoop();
  }

void keyPressed()
  {
  loop();
  }

I had initial issues without P2D and worked directly with P2D.

Advance frames with a key press.

References:

:)

I keep forgetting that I have the PDE outside of my Java project where it’s so easy to test things! Thank you for your response! While the example wasn’t exactly 1:1 with my situation I was able to adapt your code to test exactly what I was wondering about:

PGraphics a;
PGraphics b;
PGraphics other;
String renderer = P3D;

void setup() {
  size(320, 200, renderer);
  a = createGraphics(width / 2, height / 2, renderer);
  b = createGraphics(width, height, renderer);
  other = createGraphics(width / 4, height / 4, renderer);
}
  
void draw() {
  background(0); // set main window background
  
  // First phase: render (geometry & sprites)
  renderGeomSpr(other,  #ff0000);
  renderGeomSpr(a,      #00ff00);
  renderGeomSpr(b,      #0000ff);
  
  // Second phase: update (simulation state & canvas-to-canvas rendering)
  renderCanvas(other, a);
  renderCanvas(a, b);
  image(b, 0, 0);
}

void renderGeomSpr(PGraphics g, color c) {
  g.beginDraw();
  g.fill(c);
  g.rect(0, 0, g.width, g.height);
  g.fill(255);
  if (keyPressed) g.text("keyPressed=" + key, g.width / 2, g.height / 2);
  g.endDraw();
}

void renderCanvas(PGraphics source, PGraphics target) {
  target.beginDraw();
  target.image(source, 0, 0);
  target.endDraw();
}

I was able to confirm that what I expect to happen actually does happen:

  1. All 3 off-screen canvases get painted to on the geometry pass (i.e.: the individually colored rects & keyPressed strings) so they all have undergone 1 begin/end draw sequence at this point.
  2. The contents of “other” (smallest rect) are rendered overtop of the contents of “A” (mid-sized rect) during A’s 2nd begin/end draw sequence.
  3. The contents of A are rendered overtop of the contents of “B” (largest rect) during B’s 2nd begin/end draw sequence.
  4. The final image with all PGraphics contents present is rendered to the main window context as expected.

This worked for me on all renderers that I tested (P2D, P3D, JAVA2D). Much appreciated! :folded_hands: