多线程游戏程序突然锁定了glXSwapBuffers

时间:2011-02-13 00:00:38

标签: linux multithreading alsa glx

我正在完善我写的一个Linux游戏程序,在玩了大约10分钟之后,它突然减慢到每30秒左右1帧,同时减慢了整个系统的速度。即使在中断过程后,系统也会持续缓慢大约一分钟。

在多次测试中,我在发生减速时中断了GDB中的进程,并且总是在调用glXSwapBuffers的过程中。

无论游戏状态或输入如何,都会发生这种情况。阻止它的唯一方法是不在单独的线程中开始播放重复音乐曲目:线程仍在运行,但它不会不断地写入声卡缓冲区。我确保两个共享列表都已正确锁定。

是否有人遇到glXSwapBuffers和其他看似无关的线程的问题?

操作系统是Ubuntu 9,使用OpenGL的Mesa 7.6.0实现和ALSA libasound2 1.0.20-3。我今天早上为我的GeForce 6800显卡更新了我的NVIDIA驱动程序,但无济于事。

(相关?)代码如下。

显示功能:

int DisplayInterface::init()
{   
    xDisplay = XOpenDisplay(NULL);
    if (xDisplay == NULL)
    {
        printf("Error: cannot connect to the X server\n");
        return -1;
    }

    rootWindow = DefaultRootWindow(xDisplay);

    fbConfigs = glXChooseFBConfig(xDisplay, DefaultScreen(xDisplay), fbAttributes, &numConfigs);
    if (fbConfigs == NULL)
    {
        printf("Error: no X framebuffer configuration available as specified\n");
        return -1;
    }

    visualInfo = glXGetVisualFromFBConfig(xDisplay, fbConfigs[0]);
    if (visualInfo == NULL)
    {
        printf("Error: no appropriate X visual found\n");
        return -1;
    }

    colorMap = XCreateColormap(xDisplay, rootWindow, visualInfo->visual, AllocNone);
    xAttributes.colormap = colorMap;
    xAttributes.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask; // need KeyPress and KeyRelease for InputInterface

    gameWindow = XCreateWindow(xDisplay, rootWindow, 0, 0, displayWidth, displayHeight, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWColormap | CWEventMask, &xAttributes);
    XMapWindow(xDisplay, gameWindow);
    XStoreName(xDisplay, gameWindow, "Vuess Vow Vong Vo Vold Vown Vhe Vey");

    glxWindow = glXCreateWindow(xDisplay, fbConfigs[0], gameWindow, NULL);

    renderContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, NULL, GL_TRUE);
    glXMakeContextCurrent(xDisplay, glxWindow, glxWindow, renderContext);

    //glViewport(0, 0, displayWidth, displayHeight);
    glViewport(-2.0 * displayWidth, -2.0 * displayHeight, 5.0 * displayWidth, 5.0 * displayHeight);

    //glMatrixMode(GL_PROJECTION);
    //glLoadIdentity();
    //gluOrtho2D(0.0, (GLfloat)displayWidth, 0.0, (GLfloat)displayHeight);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glPixelZoom((GLfloat)((float)displayWidth / (float) pictureWidth), (GLfloat)((float)displayHeight / (float) pictureHeight));

    glClearColor((float)clearColor[0] / 255.0, (float)clearColor[1] / 255.0, (float)clearColor[2] / 255.0, (float)clearColor[3] / 255.0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    glClear(GL_COLOR_BUFFER_BIT);

    return 0;
}

// draw a Sprite from left to right and from top to bottom, starting at the given pixel
void DisplayInterface::draw(Sprite *sprite, Pixel& pixel)
{
    if (sprite == NULL)
    {
        return;
    }

    pixelstorage_t *spritePixels = sprite->getPixelData();
    const unsigned int format = sprite->getPixelFormat();

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(-2.0 * (GLfloat)displayWidth, 3.0 * (GLfloat)displayWidth, -2.0 * (GLfloat)displayHeight, 3.0 * (GLfloat)displayHeight);
    glRasterPos2i(pixel.x * (int)displayWidth / (int)pictureWidth, (int)displayHeight - (pixel.y + (int)sprite->getHeight()) * (int)displayHeight / (int)pictureHeight);
    switch (format)
    {
        case SPRITE_RGBA:
            glDrawPixels(sprite->getWidth(), sprite->getHeight(), GL_RGBA, PIXEL_TYPE, spritePixels);
    }
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

void DisplayInterface::finalizeFrame()
{   
    glFinish();
    glXSwapBuffers(xDisplay, glxWindow);
}

播放线程功能:

int writeFramesToHwBuffer(pcmsamplestorage_t *frames, snd_pcm_sframes_t numframes)
{
    int pcmreturn;

    while ((pcmreturn = snd_pcm_writei(pcm_handle, frames, numframes)) < 0)
    {
        snd_pcm_prepare(pcm_handle);
        fprintf(stderr, "Speaker Interface error: hardware buffer underrun.\n");
    }
    return pcmreturn;
}

void *playback(void *arg)
{
    int i;

    unsigned int availableframes;
    unsigned int framesFromThisBuffer;
    unsigned int framesThisTime;

    pcmsamplestorage_t *frames_mix;
    pcmsamplestorage_t *frames_track;
    unsigned int framesOffset;

    std::list<struct playbackState *>::iterator stateIter;

    while (1)
    {
        if (snd_pcm_wait(pcm_handle, 1000) < 0)
        {
            fprintf(stderr, "Speaker Interface error: poll failed.\n");
            break;
        }

        if ((availableframes = snd_pcm_avail_update(pcm_handle)) < 0)
        {
            if (availableframes == -EPIPE)
            {
                fprintf(stderr, "Speaker Interface error: an xrun occured.\n");
                break;
            }
            else
            {
                fprintf(stderr, "Speaker Interface error: unknown ALSA avail update return value (%d).\n", availableframes);
                break;
            }
        }

        // mix and write more frequently than necessary
        while (availableframes > 0)
        {
            framesThisTime = std::min(availableframes, 1024u);
            availableframes -= framesThisTime;
            //printf("Frames this time: %d / frames left to go: %d\n", framesThisTime, availableframes);

            frames_mix = new pcmsamplestorage_t[framesThisTime * 2];
            for (i = 0; i < framesThisTime * 2; i++)
            {
                frames_mix[i] = 0;
            }

            // BEGIN CRITICAL SECTION
            if (pthread_mutex_lock(&soundslists_lock) != 0)
            {
                fprintf(stderr, "Speaker Interface error: couldn't lock sounds lists from playback thread.\n");
            }

            printf("soundsPlaying has %d elements.\n", (int)soundsPlaying.size());
            printf("soundsToStop has %d elements.\n", (int)soundsToStop.size());

            for (stateIter = soundsPlaying.begin(); stateIter != soundsPlaying.end(); stateIter++)
            {
                frames_track = (*stateIter)->sound->getSamples();

                if ((*stateIter)->deliveredframes < (*stateIter)->totalframes)
                {
                    if ((*stateIter)->repeating)
                    {
                        framesFromThisBuffer = framesThisTime;
                    }
                    else
                    {
                        // mix in silence if we reach the end of this sound's buffer
                        framesFromThisBuffer = std::min(framesThisTime, (*stateIter)->totalframes - (*stateIter)->deliveredframes);                 
                    }

                    for (i = 0; i < framesFromThisBuffer * 2; i++)
                    {
                        // add samples to the mix, potentially running off the end of this buffer and wrapping around
                        if (SHRT_MAX - frames_mix[i] < frames_track[((*stateIter)->deliveredframes * 2 + i) % ((*stateIter)->totalframes * 2)])
                        {
                            // prevent overflow
                            frames_mix[i] = SHRT_MAX;
                        }
                        else if (SHRT_MIN - frames_mix[i] > frames_track[((*stateIter)->deliveredframes * 2 + i) % ((*stateIter)->totalframes * 2)])
                        {
                            // prevent underflow
                            frames_mix[i] = SHRT_MIN;
                        }
                        else
                        {
                            frames_mix[i] += frames_track[((*stateIter)->deliveredframes * 2 + i) % ((*stateIter)->totalframes * 2)];
                        }
                    }

                    (*stateIter)->deliveredframes = ((*stateIter)->deliveredframes + framesFromThisBuffer);
                    if ((*stateIter)->repeating)
                    {
                        (*stateIter)->deliveredframes = (*stateIter)->deliveredframes % (*stateIter)->totalframes;
                    }
                }
                else
                {

                    soundsToStop.push_back(stateIter);
                }
            }

            writeFramesToHwBuffer(frames_mix, framesThisTime);

            delete frames_mix;

            for (std::list<std::list<struct playbackState *>::iterator>::iterator stateiterIter = soundsToStop.begin(); stateiterIter != soundsToStop.end(); stateiterIter++)
            {
                soundsPlaying.erase(*stateiterIter);
                free(**stateiterIter);
                stateiterIter = soundsToStop.erase(stateiterIter);
            }

            if (pthread_mutex_unlock(&soundslists_lock) != 0)
            {
                fprintf(stderr, "Speaker Interface error: couldn't unlock sounds lists from playback thread.\n");
            }
            // END CRITICAL SECTION
        }
    }
}

1 个答案:

答案 0 :(得分:0)

  

操作系统是Ubuntu 9,使用OpenGL的 Mesa 7.6.0实现和ALSA libasound2 1.0.20-3。今天早上我为我的GeForce 6800显卡更新了我的 NVIDIA 驱动程序,但无济于事。

您可以使用Mesa libGL.so或NVIDIA libGL.so,但不能同时使用两者。我建议你尝试不同的OpenGL驱动程序(例如真的使用Mesa。检查glxinfo | grep OpenGL.vendor

一个疯狂的猜测:glXSwapBuffers通常会与屏幕上的垂直同步相关联,您可以尝试使用其中的选项(请参阅Google)。