Forums > Jitter

rendering multiple openGL views

February 22, 2006 | 7:40 am

Hi all -

I’ve ended up switching between a number of strategies for rendering multiple views of opengl scenes to different screens (a subject which has come up here a couple of times), and I finally decided I needed a basis of comparison of the various options. So I built the following patch + javascript, which allows a comparison of the relative speeds of four methods for rendering two views.

1. drawto: uses a single render context and switches the destination with a drawto message

2. renderContext: uses multiple render contexts, one for each view

3. viewports: uses openGL scissor_test to divide a single window into multiple viewports

4. drawpixels: draws both views, then uses glreadpixels to copy each view to a visible window. This method is clearly not useful for rendering two different views, as you have to first render both views somewhere (I’m rendering to a hidden window); it’s more useful when you have to make an exact copy of an existing window. Anyway, I included it here for completeness.

On my aging powerbook (1 GHz G4) at 1024 x 768, I get about 10fps from "drawto" with the Max window open; this goes up to about 12fps when the Max window is closed, I guess because it doesn’t have to display all those messages. "RenderContext" gives me about 12fps. "Viewports" at 1024 x 768 gives me a blank white screen; at 800 x 600 I get about 11fps. "Drawpixels" is predictably the slowest, about 6fps.

On a dual 1.8 GHz G5: "drawto" and "renderContext" both top out at about 40fps; "viewports" about 30fps; and "drawpixels" at 20fps.

So it seems that the first two methods give almost identical results, at least on these two machines. I was surprised; I expected the "drawto" method to be slower, due to all the messages.

Of the two, "renderContext" requires multiple copies of objects (one for each render context), which means a lot of extra housekeeping, especially when mixing patcher-based and javascript-based objects. Since there doesn’t seem to be much (if any) performance hit from "drawto", I’m inclined to go with that method. It would be nice if it didn’t spit out those messages, though.

I’d be curious what kinds of results other people get, also if there are other methods that anyone can think of.

- pH

max v2;
#N vpatcher 255 417 648 673;
#P window setfont "Sans Serif" 9.;
#N vpatcher 40 104 316 421;
#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P newex 50 91 30 196617 t b b;
#P newex 118 178 25 196617 s js;
#P message 87 116 33 196617 front;
#P newex 118 157 100 196617 prepend windowSet;
#P newex 70 158 44 196617 sel 1 0;
#P toggle 70 134 15 0;
#P newex 50 70 38 196617 sel 27;
#P newex 50 50 40 196617 key;
#P window linecount 2;
#P message 87 229 97 196617 ; max showmenubar;
#P message 70 198 92 196617 ; max hidemenubar;
#P outlet 87 136 15 0;
#P connect 3 0 4 0;
#P connect 4 0 10 0;
#P connect 10 1 5 0;
#P connect 5 0 6 0;
#P connect 6 0 1 0;
#P fasten 10 0 8 0 55 111 92 111;
#P connect 8 0 0 0;
#P connect 6 1 2 0;
#P fasten 5 0 7 0 75 155 123 155;
#P connect 7 0 9 0;
#P pop;
#P newobj 185 59 53 196617 p borders;
#P newex 275 190 25 196617 s js;
#P toggle 275 147 15 0;
#P message 275 167 88 196617 toggleTexture $1;
#P message 296 147 66 196617 readTexture;
#P newex 25 211 25 196617 s js;
#P newex 155 211 25 196617 s js;
#P newex 5 93 25 196617 r js;
#N thispatcher;
#Q end;
#P newobj 185 81 61 196617 thispatcher;
#P comment 7 147 99 196617 select window size:;
#P newex 25 190 101 196617 prepend windowSize;
#P user ubumenu 25 167 100 196617 0 1 1 0;
#X add 320 240;
#X add 640 480;
#X add 800 600;
#X add 1024 768;
#X add 1280 1024;
#X prefix_set 0 0 0;
#P user jit.fpsgui 106 85 60 196617 1;
#P user jit.fpsgui 42 86 60 196617 0;
#P newex 155 190 94 196617 prepend setMethod;
#P user ubumenu 152 167 100 196617 0 1 1 0;
#X add drawto;
#X add renderContext;
#X add viewports;
#X add drawpixels;
#X prefix_set 0 0
0;
#P toggle 30 28 28 0;
#P newex 30 61 51 196617 qmetro 1;
#P newex 30 120 88 196617 js multiScreen.js;
#P comment 127 147 129 196617 select rendering strategy:;
#P comment 185 42 155 196617 esc to toggle borders + menubar;
#P fasten 9 1 10 0 75 186 30 186;
#P connect 10 0 15 0;
#P connect 4 0 3 0;
#P connect 3 0 2 0;
#P fasten 13 0 2 0 10 115 35 115;
#P fasten 3 0 7 0 35 82 47 82;
#P fasten 3 0 8 0 35 82 111 82;
#P fasten 5 1 6 0 202 186 160 186;
#P connect 6 0 14 0;
#P connect 20 0 12 0;
#P connect 18 0 17 0;
#P connect 17 0 19 0;
#P connect 16 0 19 0;
#P pop;

//begin multiScreen.js
autowatch = 1;
post ("recompiledn");

var multiMethod = "drawto"
var vues = new Array;
var rand = Math.floor(Math.random()*1000);
var theContext = "context"+rand;
var angle = 0;
var wind = {width:320,height:240};
var textureFlag = 0;
var pixelWindow = new JitterObject("jit.window",theContext+2);

initAll();

function initAll() {

for (n=0;n<2;n++) {
vues[n] = {sketch:0,sketchobj:0,render:0,window:0,matrix:0,matrixFlip: 0}

vues[n].sketch = new JitterObject("jit.gl.sketch",theContext+n);
vues[n].sketch.lighting_enable = 1;
vues[n].sketch.smooth_shading = 1;

vues[n].sketchobj = new JitterObject("jit.gl.sketch",theContext+n);
vues[n].sketchobj.automatic = 0;
vues[n].sketchobj.name = "sketchobj"+theContext+n;
vues[n].sketchobj.gltranslate(-2,-2,0);
for(x=0;x<5;x++) {
for(y=0;y<5;y++) {
vues[n].sketchobj.torus(0.5);
vues[n].sketchobj.gltranslate(1,0,0);
}
vues[n].sketchobj.gltranslate(-5,1,0);
}

vues[n].qtmovie = new JitterObject("jit.qt.movie",320,240);

vues[n].qtmatrix = new JitterMatrix(4,"char",320,240);
vues[n].qtmatrix.name = "qtmatrix"+theContext+n;

vues[n].texture = new JitterObject("jit.gl.texture",theContext+n);
vues[n].texture.name = "texture"+theContext+n;

vues[n].render = new JitterObject("jit.gl.render",theContext+n);
vues[n].render.erase_color = [0,0,0,1];

vues[n].window = new JitterObject("jit.window",theContext+n);
vues[n].window.depthbuffer = 1;
vues[n].window.size = [wind.width,wind.height];
vues[n].window.pos = [(n*wind.width),50];

vues[n].matrix = new JitterMatrix(4,"char",wind.width,wind.height);
vues[n].matrix.name = "matrix"+theContext+n;

vues[n].matrixFlip = new JitterMatrix(4,"char",wind.width,wind.height);
vues[n].matrixFlip.name = "matrixFlip"+theContext+n;
vues[n].matrixFlip.usesrcdim = 0;
vues[n].matrixFlip.srcdimstart = [0,wind.height-1];
vues[n].matrixFlip.srcdimend = [wind.width-1,0];
vues[n].matrixFlip.usesrcdim = 1;
}
pixelWindow.size = [wind.width,wind.height];
pixelWindow.depthbuffer = 1;
pixelWindow.visible = 0;
}
function setMethod(a) {
multiMethod = a;
switch(multiMethod) {
case "drawto":
vues[0].sketch.gldisable("scissor_test");
vues[0].window.size = [wind.width,wind.height];
vues[1].window.visible = 1;
toggleContext(0,theContext+0);
toggleContext(1,"nowhere");
break;
case "renderContext":
vues[0].sketch.gldisable("scissor_test");
vues[0].window.size = [wind.width,wind.height];
vues[1].window.visible = 1;
toggleContext(0,theContext+0);
toggleContext(1,theContext+1);
break;
case "viewports":
vues[0].sketch.glenable("scissor_test");
vues[1].window.visible = 0;
vues[0].window.size = [wind.width*2,wind.height];
toggleContext(0,theContext+0);
toggleContext(1,"nowhere");
break;
case "drawpixels":
vues[0].sketch.gldisable("scissor_test");
toggleContext(0,theContext+2);
vues[0].window.size = [wind.width,wind.height];
vues[1].window.visible = 1;
toggleContext(1,"nowhere");
break;
}
}
function bang() {
theRotate1 = [++angle,0,1,0];
theRotate2 = [angle,1,0,0];
switch(multiMethod) {
case "drawto":
for (n=0;n<2;n++) {
toggleContext(0,theContext+n)
vues[0].sketch.reset();
vues[0].sketch.glclear();
vues[0].sketch.position = [0,0,-3];
if(n) {
vues[0].sketch.rotate = theRotate1;
} else {
vues[0].sketch.rotate = theRotate2;
}
if(textureFlag) {
vues[0].sketch.glbindtexture(vues[0].texture.name);
}
vues[0].sketch.drawobject(vues[0].sketchobj.name,1);
vues[0].render.erase();
vues[0].render.drawswap();
}
break;
case "renderContext":
for (n=0;n<2;n++) {
vues[n].sketch.reset();
vues[n].sketch.glclear();
vues[n].sketch.position = [0,0,-3];
if(n) {
vues[n].sketch.rotate = theRotate1;
} else {
vues[n].sketch.rotate = theRotate2;
}
if(textureFlag) {
vues[n].sketch.glbindtexture(vues[n].texture.name);
}
vues[n].sketch.drawobject(vues[n].sketchobj.name,1)
vues[n].render.erase();
vues[n].render.drawswap();
}
break;
case "viewports":
vues[0].sketch.reset();
vues[0].sketch.glclear();
for(n=0;n<2;n++) {
myRect = [n*wind.width,0,wind.width,wind.height];
vues[0].sketch.glviewport(myRect);
vues[0].sketch.glscissor(myRect);
vues[0].sketch.glmatrixmode("projection");
vues[0].sketch.glloadidentity();
vues[0].sketch.glfrustum([-0.8,0.8,-0.6,0.6,1,120]);
vues[0].sketch.glmatrixmode("modelview");
vues[0].sketch.glloadidentity();
vues[0].sketch.glulookat([0,0,-4,0,0,0,0,1,0]);
if(n) {
vues[0].sketch.glrotate(theRotate1);
} else {
vues[0].sketch.glrotate(theRotate2);
}
if(textureFlag) {
vues[0].sketch.glbindtexture(vues[0].texture.name);
}
vues[0].sketch.drawobject(vues[0].sketchobj.name,1)
}
vues[0].render.erase();
vues[0].render.drawswap();
break;
case "drawpixels":
vues[0].sketch.reset();
for(n=0;n<2;n++) {
vues[0].sketch.glclear();
vues[0].sketch.glmatrixmode("projection");
vues[0].sketch.glloadidentity();
vues[0].sketch.glfrustum([-0.8,0.8,-0.6,0.6,1,120]);
vues[0].sketch.glmatrixmode("modelview");
if(n) {
vues[0].sketch.rotate = theRotate2;
} else {
vues[0].sketch.rotate = theRotate1;
}
if(textureFlag) {
vues[0].sketch.glbindtexture(vues[0].texture.name);
}
vues[0].sketch.drawobject(vues[0].sketchobj.name,1)
vues[0].sketch.glreadpixels(vues[n].matrix.name);
vues[n].matrixFlip.frommatrix(vues[n].matrix.name);
vues[n].window.jit_matrix(vues[n].matrixFlip.name);
vues[0].render.erase();
vues[0].render.drawswap();
}
}
}
function windowSize(h,v) {
wind.width = h;
wind.height = v;
initAll();
setMethod(multiMethod);
}
function windowSet(a) {
for (n=0;n<2;n++) {
if(a) {
vues[n].window.pos = [(n*wind.width),0];
vues[n].window.notitle = 1;
vues[n].window.front(1);
} else {
vues[n].window.pos = [(n*wind.width),50];
vues[n].window.title = 1;
}
vues[n].window.exec;
vues[n].window.exec;
}
}
function toggleContext(source,destination) {
vues[source].sketch.drawto = destination;
vues[source].sketchobj.drawto = destination;
vues[source].texture.drawto = destination;
vues[source].render.drawto = destination;
}
function toggleTexture(a) {
textureFlag = a;
}
function readTexture(filename){
if (arguments.length==0) {
mov = vues[0].qtmovie.read();
} else {
mov = vues[0].qtmovie.read(filename);
}
if(mov[1]) {
vues[1].qtmovie.read(mov[0]);
for(n=0;n<2;n++) {
vues[n].qtmovie.matrixcalc(vues[n].qtmatrix,vues[n].qtmatrix );
vues[n].texture.jit_matrix(vues[n].qtmatrix.name);
}
}
}
//end multiScreen.js


February 22, 2006 | 8:39 am

We have made a test some while ago between drawto and multiplerender
contexts. Look for my name on the list and you will find that test. We
came to the conclusion that multiple contexts for different windows
works the best when using multiple videocards. I’m interested in the
result for this one, but I think using multiple contexts will also be
the fastest for this case as well.

Maybe I a bit biased but I always thought you should avoid as much
javascript as possible because that would slow the patch down, maybe
you could rewrite that code in a suppatcher and see the results.

Grtz


February 22, 2006 | 12:57 pm

Maarten –

OK, here’s almost the exact same example rewritten as a javascript-free patch, first as multiple render contexts, and second (in the next message) as a single render context, switching destinations with drawto.

On my PB G4, I get almost exactly the same 12fps (from both versions, with the Max window closed) that I was getting with javascript. I’ll see how it does on the G5 tomorrow, but for now, it seems to me that this disproves the idea that using javascript means taking a big speed hit.

Also, I remembered Joshua’s suggestion of turning off the jit.window sync attribute, so I tried toggling that here, but (at least on the PB) it doesn’t seem to make much difference.

- pH

[multiple render contexts example]

max v2;
#N vpatcher 206 176 1025 590;
#P origin 0 -28;
#P window setfont "Sans Serif" 9.;
#P newex 365 34 48 196617 loadbang;
#P toggle 421 284 15 0;
#P message 421 302 46 196617 sync $1;
#P newex 408 236 241 196617 jit.gl.sketch context2 @name skobj2 @automatic 0;
#P message 102 310 211 196617 reset , rotate $1 0 1 0 , drawobject skobj2 1;
#N counter 0 0 360;
#X flags 0 0;
#P newobj 102 283 83 196617 counter 0 0 360;
#P newex 102 330 285 196617 jit.gl.sketch context2 @smooth_shading 1 @lighting_enable 1;
#P newex 66 154 280 196617 jit.gl.render context2 @erase_color 0 0 0 1 @camera 0 0 6;
#P message 48 211 211 196617 reset , rotate $1 1 0 0 , drawobject skobj1 1;
#N counter 0 0 360;
#X flags 0 0;
#P newobj 48 184 83 196617 counter 0 0 360;
#P newex 48 231 285 196617 jit.gl.sketch context1 @smooth_shading 1 @lighting_enable 1;
#P user jit.fpsgui 24 69 60 196617 0;
#P window linecount 10;
#P message 365 56 385 196617 reset , gltranslate -2 -2 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5;
#P window linecount 1;
#P newex 12 47 51 196617 qmetro 1;
#P toggle 12 28 15 0;
#P newex 12 108 102 196617 t b erase b b erase b;
#P newex 12 132 280 196617 jit.gl.render context1 @erase_color 0 0 0 1 @camera 0 0 6;
#P newex 365 198 238 196617 jit.gl.sketch context1 @name skobj1 @automatic 0;
#P newex 448 350 318 196617 jit.window context2 @depthbuffer 1 @pos 1024 40 @size 1024 768;
#P newex 421 326 300 196617 jit.window context1 @depthbuffer 1 @pos 0 40 @size 1024 768;
#P window setfont "Sans Serif" 18.;
#P comment 124 53 190 196626 two render contexts;
#P connect 6 0 7 0;
#P connect 7 0 5 0;
#P connect 5 1 4 0;
#P connect 5 0 4 0;
#P connect 7 0 9 0;
#P connect 5 2 11 0;
#P connect 11 0 12 0;
#P connect 12 0 10 0;
#P connect 5 4 13 0;
#P connect 5 3 13 0;
#P connect 5 5 15 0;
#P connect 15 0 16 0;
#P connect 16 0 14 0;
#P connect 20 0 8 0;
#P connect 8 0 3 0;
#P connect 8 0 17 0;
#P connect 19 0 18 0;
#P connect 18 0 1 0;
#P connect 18 0 2 0;
#P pop;


February 22, 2006 | 12:59 pm

[part 2 of previous message]

[single render context with drawto]

max v2;
#N vpatcher 290 413 1108 847;
#P origin 0 -28;
#P window setfont "Sans Serif" 18.;
#P comment 404 40 355 196626 one render context , switch with drawto;
#P window setfont "Sans Serif" 9.;
#P message 51 128 85 196617 drawto context2;
#P message 116 154 85 196617 drawto context1;
#P message 64 354 85 196617 drawto context2;
#P message 129 331 85 196617 drawto context1;
#P newex 406 190 48 196617 loadbang;
#P toggle 195 39 15 0;
#P message 195 57 46 196617 sync $1;
#P message 38 235 293 196617 drawto context2 , reset , rotate $1 0 1 0 , drawobject skobj1 1;
#N counter 0 0 360;
#X flags 0 0;
#P newobj 38 205 83 196617 counter 0 0 360;
#P message 103 282 290 196617 drawto context1 , reset , rotate $1 1 0 0 , drawobject skobj1 1;
#N counter 0 0 360;
#X flags 0 0;
#P newobj 103 255 83 196617 counter 0 0 360;
#P newex 38 307 285 196617 jit.gl.sketch context1 @smooth_shading 1 @lighting_enable 1;
#P user jit.fpsgui 12 69 60 196617 0;
#P window linecount 10;
#P message 406 212 385 196617 reset , gltranslate -2 -2 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , gltranslate -5 1 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5 , gltranslate 1 0 0 , torus 0.5;
#P window linecount 1;
#P newex 12 47 51 196617 qmetro 1;
#P toggle 12 28 15 0;
#P newex 12 101 134 196617 t b erase b b b b erase b b b;
#P newex 51 180 280 196617 jit.gl.render context1 @erase_color 0 0 0 1 @camera 0 0 6;
#P newex 129 381 238 196617 jit.gl.sketch context1 @name skobj1 @automatic 0;
#P newex 246 113 318 196617 jit.window context2 @depthbuffer 1 @pos 1024 40 @size 1024 768;
#P newex 195 84 300 196617 jit.window context1 @depthbuffer 1 @pos 0 40 @size 1024 768;
#P connect 5 0 6 0;
#P connect 6 0 8 0;
#P connect 6 0 4 0;
#P connect 4 2 12 0;
#P connect 12 0 13 0;
#P connect 13 0 9 0;
#P connect 11 0 9 0;
#P connect 4 3 20 0;
#P connect 4 0 3 0;
#P connect 4 1 3 0;
#P connect 4 5 3 0;
#P connect 4 6 3 0;
#P connect 19 0 3 0;
#P connect 20 0 3 0;
#P connect 4 4 18 0;
#P connect 4 7 10 0;
#P connect 10 0 11 0;
#P connect 4 8 19 0;
#P connect 4 9 17 0;
#P connect 7 0 2 0;
#P connect 17 0 2 0;
#P connect 18 0 2 0;
#P connect 15 0 14 0;
#P connect 14 0 0 0;
#P connect 14 0 1 0;
#P connect 16 0 7 0;
#P pop;


February 22, 2006 | 7:13 pm

Further reports, running the patches on the G5:

The sync attribute doesn’t seem to make much difference to any of the javascript-based patch options, drawto and renderContext both run at somewhere between 35 and 40fps.

As far as the javascript-free patches: both versions (the single-context drawto version with the Max window closed, and the 2x render context version) run at about 40fps, but they do both jump up to about 45fps with sync=0.

My conclusions:

Javascript might be a tiny bit slower, but not by much. Except when you turn off sync for the jit.windows, in which case the patcher-based versions clearly have the edge (because the sync attribute doesn’t seem to have any affect on the javascript versions).

Surprisingly, multiple render contexts seems to have little or no edge over drawto.

This just in: I’ve been running ShadowKiller (http://www.unsanity.com/haxies/shadowkiller), and I just realized that when I turn shadows back on I lose about 5fps on everything. Pretty significant. Makes me wonder if there are other OS X niceties that could be switched off for extra speed…


February 22, 2006 | 8:17 pm

I’ve noticed that some widget use cpu when not activated. I always
clear out dashboard before doing something which needs my full
processor.

Also programs like menumeters or any other measuring programm uses cycles.
Just shut everything down before doing such a test.

I also have some HP software for printing on my laptop which is ported
to osx I believe. It uses alot of my cpu cycles.

In all just open activity monitor and check out what takes cycles or
memory space.



dtr
May 14, 2011 | 2:23 pm

[hyperdrive 5 years forward...]

thanks for the methods round up! here’s another one using to_texture: http://cycling74.com/forums/topic.php?id=33397


Viewing 7 posts - 1 through 7 (of 7 total)