多线程游戏程序突然锁定在glXSwapBuffers上
我正在完善自己编写的一个Linux游戏程序,玩了大约10分钟后,它突然减慢到每三十秒一帧左右,整个系统也变慢了。即使中断该过程后,系统仍会缓慢运行约一分钟。
在多次测试中,当速度变慢时,我会中断 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
}
}
}
I'm polishing up a Linux game program I wrote, and after about 10 minutes of playing, it suddenly slows down to 1 frame per thirty seconds or so, slowing the entire system down as well. Even after interrupting the process, the system continues to be slow for about a minute.
In multiple tests I've interrupted the process in GDB when the slowdown occurs, and it is always in the middle of a call to glXSwapBuffers
.
It happens regardless of game state or input. The only thing that prevents it is not beginning playback of a repeating music track in a separate thread: the thread still runs, but it doesn't constantly write to the sound card buffer. I've ensured that two shared lists are properly locked.
Has anybody run into a problem with glXSwapBuffers
and other, seemingly unrelated threads?
The OS is Ubuntu 9, using the Mesa 7.6.0 implementation of OpenGL and ALSA libasound2 1.0.20-3. I updated my NVIDIA drivers for my GeForce 6800 graphics card this morning, but to no avail.
(Relevant?) code follows.
Display functions:
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);
}
Playback thread functions:
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
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以使用 Mesa libGL.so 或 NVIDIA libGL.so,但不能同时使用两者。我建议你尝试不同的OpenGL驱动程序(例如真正使用Mesa。检查
glxinfo | grep OpenGL.vendor
)作为一个疯狂的猜测:
glXSwapBuffers
通常会与垂直同步接口您的屏幕上,您可以尝试使用相应的选项(请参阅Google)。You can be using a Mesa libGL.so, or NVIDIA libGL.so, but not both at the same time. I suggest you try a different OpenGL driver (e.g. really use Mesa. Check
glxinfo | grep OpenGL.vendor
)As a wild guess:
glXSwapBuffers
often will interface with the vertical sync on your screen, you might try playing with options for that (see Google).