Smoothing in OS X Lion

Hi There,
I’ve just switched to Mac OS 10.7 (using OF 0.062) and my app often crashes when smoothing is enabled. I call ofEnableSmoothing() in the setup function, and it only crashes when there is a lot of action on the screen.
Depending on the run, I get a bad access at various places in the code, but always at the same OpenGL function, always with a NoFill() function called before it. Crash log excerpt :

7 GLEngine 0x04cf7768 gleDoDrawDispatchCore + 454
8 GLEngine 0x04c99a8a glDrawArrays_IMM_Exec + 231
9 libGL.dylib 0x91fbc20c glDrawArrays + 44)

Will OF 0.07 solve this problem?
Would it be preferable to enable smoothing and to disable it when not necessary like alpha blending?

Thanks

in general it’s good practice to enable/disable things like smoothing and alpha blending as you need them. it can help speed up rendering.

how often does this crash happen?

can you post a short snippet of code (testApp.cpp) that crashes for you?

then we can test on 007 to see if we have the same problem.

Hi,

Here’s the most common cases :

case 1

my app calls ofEndShape();

which calls glDrawArrays(GL_LINE_STRIP, 0, numToDraw);

which calls

  
  
0x97af01e0  <+0000>  push   %ebp  
0x97af01e1  <+0001>  mov    %esp,%ebp  
0x97af01e3  <+0003>  sub    $0x18,%esp  
0x97af01e6  <+0006>  mov    %gs:0x78,%eax  
0x97af01ec  <+0012>  mov    (%eax),%ecx  
0x97af01ee  <+0014>  mov    0x10(%ebp),%edx  
0x97af01f1  <+0017>  mov    %edx,0xc(%esp)  
0x97af01f5  <+0021>  mov    0xc(%ebp),%edx  
0x97af01f8  <+0024>  mov    %edx,0x8(%esp)  
0x97af01fc  <+0028>  mov    0x8(%ebp),%edx  
0x97af01ff  <+0031>  mov    %edx,0x4(%esp)  
0x97af0203  <+0035>  mov    %ecx,(%esp)  
0x97af0206  <+0038>  call   *0x108(%eax)  
0x97af020c  <+0044>  add    $0x18,%esp  BAD ACCESS HERE  
0x97af020f  <+0047>  pop    %ebp  
0x97af0210  <+0048>  ret     
  

case 2

testApp.cpp calls ofLine();

which calls glDrawArrays(GL_LINES, 0, 2);

which calls

  
  
0x97af01e0  <+0000>  push   %ebp  
0x97af01e1  <+0001>  mov    %esp,%ebp  
0x97af01e3  <+0003>  sub    $0x18,%esp  
0x97af01e6  <+0006>  mov    %gs:0x78,%eax  
0x97af01ec  <+0012>  mov    (%eax),%ecx  
0x97af01ee  <+0014>  mov    0x10(%ebp),%edx  
0x97af01f1  <+0017>  mov    %edx,0xc(%esp)  
0x97af01f5  <+0021>  mov    0xc(%ebp),%edx  
0x97af01f8  <+0024>  mov    %edx,0x8(%esp)  
0x97af01fc  <+0028>  mov    0x8(%ebp),%edx  
0x97af01ff  <+0031>  mov    %edx,0x4(%esp)  
0x97af0203  <+0035>  mov    %ecx,(%esp)  
0x97af0206  <+0038>  call   *0x108(%eax)  
0x97af020c  <+0044>  add    $0x18,%esp BAD ACCESS HERE  
0x97af020f  <+0047>  pop    %ebp  
0x97af0210  <+0048>  ret    
  

Might I add that the app crashes only when these two conditions are met :

  1. smoothing is enabled
  2. there is a lot of action on the screen

then it will take about 2 or 3 seconds to crash. It never crashes at the same place in the code, but it’s always a bad access at a shape that needs smoothing, and always at the same line of code posted above.

My GL driver is ATIRadeonX3000GLDriver, in case it can help.

Maybe I forgot something obvious somewhere, but it works perfectly on OS 10.6 and on OS 10.7 without smoothing.

i just stared for a while at the code around ofEndShape() that you’re talking about, and i can’t figure out what might be wrong. here’s one shot in the dark, try adding this line:

  
  
glDisableClientState(GL_VERTEX_ARRAY);  
  

to ofEndShape(), so you get:

  
  
...  
glEnableClientState(GL_VERTEX_ARRAY);  
glVertexPointer(2, GL_FLOAT, 0, &points[0]);  
glDrawArrays(GL_LINE_STRIP, 0, numToDraw);  
glDisableClientState(GL_VERTEX_ARRAY);  
...  
  

again, please post a minimal chunk of code. otherwise it’s impossible to gauge exactly what you mean by “a lot of action on the screen”. are we talking about a dozen FBOs, a handful of 1M vertex VBOs, half a dozen shaders…? or just running without vsync and drawing lots of lines?

first, many thanks for your patience.

I think I found the culprit.

It’s a function for sound waveform display (using the technique in audio input and output examples). There’s a maximum of 6 sounds that can be displayed simultaneously on a width of 130 pixels each (potentially 780 ofLine total). With this function commented out, the app never crashes. Disabling smoothing before drawing those lines and re-enabling it after works perfectly, and is graphically acceptable (not ugly).
(Also, glDisableClientState doesn’t seem to change anything.)

Here’s the code :

I’m using this OpenAL sound player :
http://forum.openframeworks.cc/t/new-openal-based-ofsoundplayer/2920/0

and added those functions for the waveform display :

in ofxSoundPlayer.h

  
  
    void generateWaveform();  
    void drawWaveform();  
    void displayWaveform(float, float, int);  
    bool display;  
    int index;  
    short direction; // 1 or -1 or mirror display  
    float px, py;  
    vector<short>sndsamples  
  

in ofxSoundPlayer.cpp

  
  
// called when the sound is loaded  
void ofxSoundPlayer::generateWaveform(){  
    for (int i=0; i<(MIN(700*30,_engine->buffer->GetSamplesCount())); i +=30){ // get 1 sample every 30 samples : accurate enough for our display. The sndsamples list's maximum is 700.  
        sndsamples.push_back((short)_engine->buffer->GetSamples()[i]);  
        sndsamples.back() = sndsamples.back()/32786.0*40; // display on a 40px height  
    }  
}  
  
// called in keyPressed() or any user related action  
void ofxSoundPlayer::displayWaveform(float posx, float posy, int tdirection){  
    px=posx;  
    py=posy;  
    direction=tdirection;  
    display = true;  
    index=0;  
}  
  
// called in draw()  
void ofxSoundPlayer::drawWaveform(){  
    if (display==false) return;  
    if (index+130<sndsamples.size()){ // display on a 130px width  
	ofDisableSmoothing();  
        ofSetColor(0xFFFFFF);  
        for (int j=0; j<130; ++j){  
            ofLine(px+direction*j,py,px+direction*j,py+(sndsamples[index+j]));  
        }  
	ofEnableSmoothing();  
        if (ofGetFrameNum()%2==0)index=index+40;  
    }  
    else {  
        display=false;  
    }  
}  
  

i think what you’re experiencing might actually be a threading bug.

when you hit keyPressed() or do other things, it’s happening in a separate thread as the opengl drawing from draw(). this means that a function like:

  
  
for (int j=0; j<130; ++j){    
ofLine(px+direction*j,py,px+direction*j,py+(sndsamples[index+j]));    
}  
  

can be interrupted and have it’s behavior changed by keyPressed() in the middle of the for loop.

similarly if the sound loading is threaded, it can cause draw() to crash (i’m not familiar with OpenAL).

in general, these things should cause crashes cross-platform though, so i’m not sure why it’s only on 10.7.

that said, i’m glad you can at least avoid the crash now :slight_smile:

that’s a very good thing to know.

Every sound and waveform is loaded/generated in the setup() function before draw().
Therefore, I think the only things the user can alter during the loop are the waveform position and index (I don’t think that’s a problem).
Does calling everything in setup() prevents a potential threading problem?

assuming that the loading is not threaded, yes, that prevents any potential threading problems with loading. i’m pretty sure the default OF sound loader is not threaded.

the keypressed function could still cause problems, theoretically, but i don’t see any obvious way for that to happen here.

have we fixed EXC_BAD_ACCESS bug when you do ofEndShape() without having called anything in between?
i fixed this on a branch somewhere, but it’s easier for a dev to fix than make it through the pull request assault course.

hey elliot, that was a bug during 007 development but should be fixed in the 007 release. there were a number of places in the GL code that didn’t check for the vertex array size before trying to draw. i hunted all of them down and added checks, and arturo added some as well i think.

a small update :

I upgraded to OF 0.07 and now I call the waveform display func directly in draw() (the function is not inside the sound player anymore to prevent every possible thread bug). Unfortunately, the crash still occurs when the waveforms are smoothed. Same place, same reason.

Sometimes, it even occurs when the waveform smoothing is disabled (although it is very rare - happened only twice during dozens of hours of runtime).

At least, non smoothed waveforms look much better in 0.07. A handful of minor glitches disappeared too. Whatever you guys did, thanks for that.

Let me know if you find anything.

Don’t think the smoothing has nothing to do but something with threads, where are you calling generateWaveform? if it’s being called from a different thread not one you’ve created but a thread created by openal for example, you should be using a mutex like:

  
  
.h  
ofMutex mutex;  
  
.cpp  
void ofxSoundPlayer::generateWaveform(){  
    mutex.lock();  
    for (int i=0; i<(MIN(700*30,_engine->buffer->GetSamplesCount())); i +=30){ // get 1 sample every 30 samples : accurate enough for our display. The sndsamples list's maximum is 700.  
        sndsamples.push_back((short)_engine->buffer->GetSamples()[i]);  
        sndsamples.back() = sndsamples.back()/32786.0*40; // display on a 40px height  
    }  
    mutex.unlock();  
}  
  
  
// called in draw()  
void ofxSoundPlayer::drawWaveform(){  
    if (display==false) return;  
    mutex.lock();  
    if (index+130<sndsamples.size()){ // display on a 130px width  
	ofDisableSmoothing();  
        ofSetColor(0xFFFFFF);  
        for (int j=0; j<130; ++j){  
            ofLine(px+direction*j,py,px+direction*j,py+(sndsamples[index+j]));  
        }  
	ofEnableSmoothing();  
        if (ofGetFrameNum()%2==0)index=index+40;  
    }  
    else {  
        display=false;  
    }  
    mutex.unlock();  
}  
  

btw. the key and mouse event functions don’t happen in a different thread, not even in an interruption. GLUT traps the hardware interruptions that happen in the update/draw cycle store the events in a queue and then calls the key and mouse callbacks before the next update.

[quote=“arturo, post:13, topic:7239”]btw. the key and mouse event functions don’t happen in a different thread, not even in an interruption. GLUT traps the hardware interruptions that happen in the update/draw cycle store the events in a queue and then calls the key and mouse callbacks before the next update.
[/quote]

wow! that’s great news. i can totally drop the pattern i always use:

  
  
void testApp::keyPressed(int key) {  
  if(key == ' ') {  
    needToDoSomething = true;  
  }  
}  
void testApp::update() {  
  if(needToDoSomething) {  
    needToDoSomething = false;  
    doSomething();  
  }  
}  
  

can now just be:

  
  
void testApp::keyPressed(int key) {  
  if(key == ' ') {  
    doSomething();  
  }  
}  
void testApp::update() {  
}  
  

thanks for the info about user input.

When I upgraded to 0.07, I implemented the drawWaveform() function in one of my classes that displayed the sound’s information graphically instead of leaving it inside the OpenAL soundplayer itself. This function inside the class was then called in testApp::draw(). This did not solve the problem.

Today I kinda lost my temper and, with function returns, I managed to work my way up and implement the drawWaveform() function directly in testApp (testApp::drawWaveform).

That solved it.

The code is not as clean, but I can live with that. I think this proves it was a thread bug.
Perhaps it’s time that I google/read a couple of things about threading and mutexes to make sure this which hunt doesn’t happen again.

One last question while I’m at it :
My testApp::draw() function calls myOtherClasses::draw() functions. I’ve never used threads or mutexes in them. Should I?

no, everything you call from setup/update/draw or the key/mouse events functions happens in the opengl thread. if you are using ofSoundStream then everything you call from the audioIn/audioOut functions happens in the audio thread and you’ll probably need mutexes or some buffering technique. when you say the openAL sound player you mean ofxOpenALSoundPlayer right? not sure if that exposes any different thread.