Performance loss with PGraphics and SVG

Hi there. I’m having a large issue with performance loss using PGraphics.
The situation is: I’m importing an svg with 860 child Polygons. I need to do a color sampling for each and every child shape based on it’s centroid. The final file is of large dimension(2400,1920).

What i have now: when i get everything running outside the PGr i get over 45 frames/second. if i encapsulate it in a PGr i get 9 and it starts to decrease the frameRate. So actualy i get no performance enhancement for offscreen buffer.

P.S. I have 10 images for colorsampling, the images are full scale(2400,1920). in the next phase it will be a video, a shader, or another interaction.

where did i get it worng?

here’s the code. PGr operations are commented

import java.util.Map;
ArrayList<Brick> bricks;
HashMap<Brick, Integer> colorMap;

PShape original;
PGraphics pg;
PImage[] images=new PImage[10];
int imageIndex=6;
PImage colorSampler;

int w=2400;
int h=1920;

void setup() {
  size(1200, 960, P2D);
  smooth(8);
  frameRate(30);

  pg=createGraphics(w, h);
  colorSampler=createImage(w, h, RGB);
  loadImages();
  colorSampler=images[imageIndex];

  bricks=new ArrayList<Brick>();
  buildBrick();
  colorMap=new HashMap<Brick, Integer>();
  buildColorMap();
  getColor();

}

void draw() {

  image(colorSampler, 0, 0);

  //pg.beginDraw();
  //pg.clear();

  getColor();

  for (Brick b:bricks) {
    b.update();
    b.display();
  }

 //pg.endDraw();

  fill(255, 0, 0, 100);
  text(frameRate, 10, 10);
}

void buildBrick() {
  original=loadShape("tijolos2.svg");
  original.disableStyle();
  int id=0;
  int numChild=original.getChildCount();
  println(numChild);

  for (int i=0; i<numChild; i++) {
    PShape tempFace=original.getChild(i);
    int numBricks=tempFace.getChildCount();

    for (int j=0; j<numBricks; j++) {
      PShape temp=tempFace.getChild(j);

      bricks.add(new Brick(id));

      int vCount=temp.getVertexCount();

      for (int k=0; k<vCount; k++) {
        Brick b=bricks.get(bricks.size()-1);
        PVector pv=temp.getVertex(k);
        b.addPoint(pv);
      }

      Brick b=bricks.get(bricks.size()-1);
      b.getCentroid();
      b.buildShape();

      id++;
    }
  }
}

void buildColorMap() {
  for (Brick b : bricks) {
    colorMap.put(b, b.c);
  }
}

void getColor() {
  colorSampler.loadPixels();

  for (Brick b : bricks) {
    color c=color(red(colorSampler.pixels[b.loc]), green(colorSampler.pixels[b.loc]), blue(colorSampler.pixels[b.loc]));
    colorMap.put(b, c);
  }
}

void loadImages() {
  for (int loadCounter=1; loadCounter<=10; loadCounter++) { 
    images[loadCounter-1]=loadImage("sample0"+loadCounter+".jpg");
  }
}

void keyPressed() {
  if (key==CODED) {
    if (keyCode==RIGHT) {
      if (imageIndex<images.length-1) {
        imageIndex++;
      } else {
        imageIndex=0;
      }
      println("RIGHT:  "+imageIndex);
      colorSampler=images[imageIndex];
    }
    if (keyCode==LEFT) {
      if (imageIndex>0) {
        imageIndex--;
      } else {
        imageIndex=images.length-1;
      }
      println("LEFT:  "+imageIndex);
      colorSampler=images[imageIndex];
    }
  }
}

Brick object

class Brick {
  PShape s, centroid;
  ArrayList<PVector> vertices;
  PVector center;
  int centerX, centerY;
  int id;
  color c;
  int loc;

  Brick(int _id) {
    s=createShape();
    vertices=new ArrayList<PVector>();
    center=new PVector();
    id=_id;
    c=color(0);
  }

  void addPoint(PVector p) {
    vertices.add(p);
  }

  void getCentroid() {
    for (PVector p : vertices) {
      centerX+=p.x;
      centerY+=p.y;
    }
    centerX/=vertices.size();
    centerY/=vertices.size();

    loc=int(centerX+centerY*colorSampler.width);
  }

  void buildShape() {
    s.beginShape();
    s.fill(c, 120);
    s.strokeWeight(1);
    s.stroke(255);
    for (PVector p : vertices) {
      s.vertex(p.x, p.y);
    }
    s.endShape(CLOSE);

    centroid=createShape(ELLIPSE, centerX, centerY, 8, 8);
  }

  void update() {
    c=colorMap.get(this);
  }


  void display() {
    /*
    pg.beginDraw(); 
    pg.strokeWeight(1);
    s.setFill(color(c, 150));
    pg.shape(s);


    pg.strokeWeight(4);
    centroid.setStroke(color(255));
    centroid.setFill(color(c));
    pg.shape(centroid);

    pg.endDraw();
    */

    strokeWeight(1);
    s.setFill(color(c, 150));
    shape(s);

    strokeWeight(4);
    centroid.setStroke(color(255));
    centroid.setFill(color(c));
    shape(centroid);
  }
}
1 Like

Try createGraphics(w, h, P2D);

no difference at all

OK, just an obvious potential problem / difference, because one is using Java2D and one is using P2D.

still don’t get where the problem lies, since i’m not using svg in runtime, but an arraylist of polylines that contains one arraylist of PVectors and 6 vectors is the greatest of them all.

Do you need to do everything you are doing inside draw() on every frame? Or can some of those calculations be done once inside setup()?

This is not very fast:

 color c=color(red(colorSampler.pixels[b.loc]), green(colorSampler.pixels[b.loc]), blue(colorSampler.pixels[b.loc]))

Why not just

 color c=colorSampler.pixels[b.loc];

? If you need each component, look in the documentation of red(), green() and blue() for faster bit-shifting based alternatives.

If your main screen is 1200x960, why do you need the 10 other images to be 2400x1920?

1 Like

hi
1.
i’m trying to use video or a live feed to do the color sampling. with still images, color sampling just needs to happen once.

  1. tried the second option also with no gain at all

my final output will be a 2400x1920 screen(two vertical rotated 90º)

till now, i’m trying the render solution instead of a live one. with render i mean saving each frame.

but just in case. performance sucks anyway, even when not calling the color sampling.
just plain svg runs at 44fps instead of the usual 60

It’s hard to suggest something without understanding the logic :slight_smile: In the final program… there will be a video instead of 10 images? What’s the purpose of the svg? What is the goal of the program?

You can profile the application using https://visualvm.github.io/ and it could tell which methods are consuming most CPU.

By looking at another of your questions I get the impression you were using openFrameworks before. Why did you switch (if you did)?

Hmmm but the svg is not going to change on every frame right? Could you draw it into a PGraphics just once, so you work with pixels instead of rendering vector graphics to pixels on every frame?

the objective is to do a live mapping on a weird surface.
i’ve got a gothic dome that is make of bricks. each shape is a brick. all 880 bricks/shapes are in the vector file. i’m projecting over these bricks with two projectors. my workflow is generative -> syphon -> madmapper.
so for instance, i can project something like a growing circle, or a particle system that emits circles that grow. i can simply do that. but instead i can light up the brick if some part of the circunference is passing through the brick centroid. actually this is what i’m doing. but i have to render the videos instead of doing something live(vj).
i’d rather do something live/generative than doing renders. after all, this is why i’ve migrated to programming.
i learned to program with processing, so this is the environment where i work faster. openFramworks is awesome, but i’m slow with it, i get lost in how to do things instead of doing things. i intend to prototype in processing than translate it to oF.

following images are what madmapper spatial scan delivers. one for each projector
projector1


projector 2

2 Likes

also. the same svg gets 44fps in oF too.

In debug or release mode? Release can perform much faster.

I would use visualvm to find the bottleneck. Otherwise you can do random attempts at optimizing, but you may or may not find the bottleneck.

Random ideas:

  • Check how many vertices each brick has. Maybe they are too detailed.
  • Does the computer have good CPU and GPU?
  • Do you need stroke() in the bricks? That involves more work than just fill()
  • Calling color() each time is not necessary if the color doesn’t change (for instance you could store color(255) in a variable called white.
  • I believe calling .setStroke() and .setFill() iterates over each vertex and changes its properties. Using a shader to affect rendering might be faster (if the bottleneck is CPU bound).
1 Like

I’m going to hazard a guess – just a guess! – that you could get a large performance boost by ditching the PShape Brick.s and the use of shape() in Brick.display() entirely, and switching to just keeping the vertex list and using quad().

https://processing.org/reference/quad_.html

i tought that if i built the brick once, and then display it i wouldn’t loose performance by building the brick every cycle.
quad might work, but sometimes, specially in the corners i have three, fice or six vertices per brick.

You get most performance from cached shapes when you draw them in one go under one parent, rather than drawing lots of small ones individually. Simpler shapes will probably draw quicker without using PShape.

Can you replace the color map entirely with texture coordinates on the vertexes for each brick to read from the color? Or with custom attributes and shaders. Let the GPU do the work.

i still lack the knowledge to create my own custom shaders. and i agree it would be much faster to get the color for each desired coordinate from shaders.

If it’s just the colour from an input texture, you should be able to do this with texture coordinates on the vertexes of the shape and no custom shader though.

will have to read how to get this done.