Off-screen render performance, multiple PGraphics objects

how can reduce the time taken to draw all my components?

as, specifically, PGraphics.beginDraw() and PGraphics.endDraw() calls are expensive if there are a lot of them in a single call

as if i do 400 by 400
i get 60 fps

if i do fullscreen i get half that

however if i do not add any applications then i get 60fps in fullscreen

with 0 applications i get 60
with 1 i get 54.9
with 2 i get 43
with 3 i get 36
with 4 i get 31
with 5 i get 26

and so on

how can i avoid this while still retaining the ability to off-screen render?

the following is a complete minimal example of this

Compositor compositor;

void settings() {
  fullScreen(P3D); // 31 fps
  //size(400, 400, P3D); // 60 fps
}

class Applications_Cube extends Window {

  @Override
    void draw() {
    graphics.lights();
    graphics.background(0);
    graphics.noStroke();
    graphics.translate(width/2, height/2);
    graphics.rotateX(frameCount/100.0);
    graphics.rotateY(frameCount/200.0);
    graphics.box(40);
  }
}

void addApplications() {
  compositor.add(new Applications_Cube(), 200, 200);
  compositor.setLocation(0, 0);
  compositor.add(new Applications_Cube(), 200, 200);
  compositor.setLocation(0, 200);
  compositor.add(new Applications_Cube(), 200, 200);
  compositor.setLocation(200, 0);
  compositor.add(new Applications_Cube(), 200, 200);
  compositor.setLocation(200, 200);
}

void setup() {
  compositor = new Compositor(width, height);
  compositor.displayFPS = true;
  addApplications();
  compositor.setup();
}

void draw() {
  compositor.draw();
}

// TODO: rename to Window manager and split to WindowManager and Compositor
// for now, assume that the compositor draws what the window manager renders
// however this may be incorrect, so avoid doing this for the time being
// untill more information is available on the difference between a
// compositor and a window manager

// from https://github.com/mgood7123/AndroidCompositor/blob/5232f327e22df10368c780287822bc2320b22f06/app/src/main/jni/compositor.cpp#L17
// my current understanding of all this is that a compositor will render each
// application's frame buffer, and a window manager such as KDE or GNOME or I3,
// will work WITH the compositor retrieving information about windows and their
// position, then draw boarders around those windows and implement either stacking
// or tiling like functionality depending on the windowing system type and assumably
// send information back to the compositor such as updates on window changes.

// for example if the window is minimized or its position changes, the compositor
// will then redraw itself as it sees fit according to the received information

// end from https://github.com/mgood7123/AndroidCompositor/blob/5232f327e22df10368c780287822bc2320b22f06/app/src/main/jni/compositor.cpp#L17

// TODO: optimize focus algorithm

class Compositor {
  public PGraphics graphics;
  ArrayList<WindowObject> windows = new ArrayList<WindowObject>();
  WindowObject w;
  int windowFocus = -1;
  int lastWindowFocus = -1;
  boolean displayFPS = false;

  Compositor(int width, int height) {
    graphics = createGraphics(width, height, P3D);
  }

  void add(Window window, int width, int height) {
    w = new WindowObject(width, height);
    windows.add(w);
    w.attach(window);
  }

  void setLocation(int x, int y) {
    w.x = x;
    w.y = y;
  }

  void drawGraphics() {
    if (displayFPS) {
      graphics.beginDraw();
      int oldColor = graphics.fillColor;
      graphics.fill(255);
      graphics.textSize(16);
      graphics.text("FPS: " + frameRate, 10, 20);
      graphics.fill(oldColor);
      graphics.endDraw();
    }
    image(graphics, 0, 0);
  }

  void drawGraphics(WindowObject window) {
    graphics.image(
      window.graphics, 
      window.x, 
      window.y, 
      window.width, 
      window.height
    );
  }

  void setup() {
    graphics.beginDraw();
    for (WindowObject window : windows) {
      window.setup();
      drawGraphics(window);
    }
    graphics.endDraw();
    drawGraphics();
  }

  void draw() {
    graphics.beginDraw();
    graphics.background(0);
    for (WindowObject window : windows) {
      window.draw();
      drawGraphics(window);
    }
    graphics.endDraw();
    drawGraphics();
  }
}

class Window {
  public PGraphics graphics = null;

  int height;
  int width;
  int x, y;
  int mouseX, mouseY;
  Window() {
  } // implicit super constructor required
  void onBeforeResize() {
  }
  String onRequestType() { 
    return P3D;
  }
  void onAfterResize() {
  }
  void setup() {
    graphics.background(0);
  }
  void draw() {
    graphics.background(0);
  }
}

class WindowObject {
  public PGraphics graphics;
  Window window;

  int height;
  int width;
  int x;
  int y;

  int borderTop = 20;
  int borderLeft = 3;
  int borderBottom = 3;
  int borderRight = 3;

  WindowObject() {
  } // implicit super constructor required

  WindowObject(int width, int height) {
    this.width = width;
    this.height = height;
    graphics = createGraphics(width, height, P3D);
  }

  void attach(Window window) {
    this.window = window;

    this.window.x = borderLeft;
    this.window.width = width-borderLeft-borderRight;

    this.window.y = borderTop;
    this.window.height = height-borderTop-borderBottom;
  }

  void clearScreen() {
    graphics.background(0);
  }

  void drawBordersWithFill(int fill__) {
    graphics.rectMode(CORNER);
    graphics.stroke(0);
    graphics.fill(fill__);
    graphics.rect(0, 0, width, height, 10);
  }
  void drawBorders() {
    drawBordersWithFill(87);
  }

  void drawGraphics() {
    graphics.endDraw();
    window.graphics.beginDraw();
    // draw fps
    window.graphics.endDraw();
    graphics.beginDraw();
    graphics.image(window.graphics, window.x, window.y, window.width, window.height);
  }

  void drawWindow() {
    graphics.beginDraw();
    clearScreen();
    drawBorders();
    drawGraphics();
    graphics.endDraw();
  }

  void resizeWindow() {
    String type = window.onRequestType();
    window.onBeforeResize();
    window.graphics = createGraphics(window.width, window.height, type);
    window.onAfterResize();
  }

  void setup() {
    resizeWindow();
    window.graphics.beginDraw();
    window.setup();
    window.graphics.endDraw();
    drawWindow();
  }

  void draw() {
    window.graphics.beginDraw();
    window.draw();
    window.graphics.endDraw();
    drawWindow();
  }
}