I have a large project that uses blendMode(SCREEN). I call the blendMode() function once in setup(). I am using the FX2D renderer, but the issue also presents itself in P2D mode as well.
On my Windows 10 pc, the project runs fine at 60fps. The exact same project runs around 1-5 fps when compiled and run on my Mac at home. If I remove the blendMode directive, the project runs at 60fps on the Mac.
I have also noticed similar projects slow down to crawl when I attempt to use blendMode(). These are all giant projects so I canāt really post the code. I have yet to build a simple example that exhibits the same problem.
Can someone help me understand what might be happening? Thanks!
My experience with blendMode is that it often slows down my sketches. This isnāt only with Processing thoughā the same happens when Iām blending layers in Photoshop. The size of the stage, the amount of details, and gradients are some examples that greatly influence the performance.
Itās just a guess, but I think that youāre more likely to win speed by optimizing your code. Or perhaps there is way to write a function that simulates the blend mode, but is somewhat a bit more efficient.
Here is an example I wrote to demonstrate the problem I am having.
My AMD Threadripper and 1080 graphics I see
80,000 ellipses @60fps blendMode(NORMAL)
2,800 ellipses @60fps blendMode(SCREEN)
Perhaps this is expected behavior. I would like to screen all these objects without loss of frameRate. Can I eliminate alpha or draw objects differently to help blend faster?
// BlendMode Test
// Thousands of semi-transparent colored ellipses randomly move around the screen. Simulation continues to add ellipses and monitor frameRate while user can toggle blendModes for effect.
// Jared S Tarbell
// November 12, 2019
// Levitated Toy Factory
// Albuquerque, New Mexico USA
//
// Processing 3.5.3
ArrayList<Ball> balls = new ArrayList<Ball>();
IntList history = new IntList();
long[] times;
int averageOverFrames = 5;
long baseTime;
int max = 100000;
long lastMillis = 0;
boolean doBlend = false;
void setup() {
size(1000,1000,FX2D);
background(0);
times = new long[averageOverFrames];
baseTime = millis();
for (int i=0;i<averageOverFrames-1;i++) times[i] = baseTime;
}
void mousePressed() {
// toggle blendmode
doBlend = !doBlend;
if (!doBlend) blendMode(NORMAL);
}
void draw() {
// clear the background and set the blend mode or not
background(0);
if (doBlend) blendMode(SCREEN);
// draw all the balls
for (Ball b:balls) b.render();
// make new balls to reach maximum number
for (int k=0;k<2;k++) {
if (balls.size()<max) {
Ball neo = new Ball();
balls.add(neo);
}
}
// render framerate history
pushMatrix();
stroke(0,255,0,222);
noFill();
int starti = 0;
if (history.size()>width) {
//scale((1.0*width)/history.size());
translate(width-history.size(),0);
starti = history.size()-width;
}
beginShape();
for (int i=starti;i<history.size();i++) vertex(i,height-history.get(i));
endShape();
popMatrix();
// draw 60fps benchline
stroke(255,128);
line(0,height-60,width,height-60);
// calculate framerate and build history
float f = calcFrameRate();
history.append(round(f));
// calculate elapsed time
float secs = (millis()-baseTime)/1000.0;
// report framerate and other metrics
fill(255);
noStroke();
textSize(20);
textAlign(LEFT);
String rpt = balls.size()+" balls @"+round(f)+"fps "+nf(secs,0,1)+"s ";
if (doBlend) rpt+="SCREEN";
else rpt+="NORMAL";
text(rpt,10,height-20);
textAlign(RIGHT);
text("mousePress to toggle blendMode",width-10,height-20);
}
float calcFrameRate() {
// calculate the average frame rate over the last 5 frames
for (int i=0;i<averageOverFrames-1;i++) times[i] = times[i+1];
times[averageOverFrames-1] = millis();
long totalMillis = times[averageOverFrames-1] - times[0];
if (totalMillis<=0) return 0; // something is weird
return (averageOverFrames-1)*1000.0/totalMillis;
}
class Ball {
float x, y;
float vx, vy;
float w;
color myc;
Ball() {
x = random(width);
y = random(height);
vx = random(-3,3);
vy = random(-3,3);
w = random(2,20);
myc = color(random(255),random(255),random(255));
}
void render() {
fill(myc);
noStroke();
ellipse(x,y,w,w);
x+=vx;
y+=vy;
// bound check
if (x<0) x = width;
if (w>width) x = 0;
if (y<0) y = height;
if (y>height) y = 0;
}
}
This is correct ā by accident. NORMAL is defined in PConstants as ā1ā. The default blendMode, BLEND, is also defined as ā1ā. So this should be written āblendMode(BLEND)ā ā¦but it does the same thing.
The scale of the difference is a bit surprising, although it is not surprising that SCREEN is slower. In primitive operations, often addition and subtraction are faster than multiplication is faster than division. SCREEN is division based, often the slowest operation. Still, usually not 40x slower. In addition to benchmarking, it might be worth looking at the underlying implementation.