Allegro program triggers breakpoint? - sprite

So I'm learning about sprite programming and we're using allegro. When I run one of the sample programs I get the message: rotatesprite.exe has triggered a breakpoint. This was in visual studio. I can't get allegro to work outside of visual studio
sample program:
#include <allegro.h>
#define WHITE makecol(255,255,255)
int main(void)
{
int x, y;
float angle = 0;
BITMAP *tank;
//initialize program
allegro_init();
install_keyboard();
set_color_depth(32);
set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
textout_ex(screen,font,"Rotate: LEFT / RIGHT arrow keys",
0,0,WHITE,0);
//load tank sprite
tank = load_bitmap("C:\Users\Jason\Desktop\module7\tank.bmp", NULL);
//calculate center of screen
//x = SCREEN_W/2 - tank->w/2;
//y = SCREEN_H/2 - tank->h/2;
x=SCREEN_W/2;
y=SCREEN_H/2;
//draw tank at starting location
rotate_sprite(screen, tank, x, y, 0);
//main loop
while(!key[KEY_ESC])
{
//wait for keypress
if (keypressed())
{
//left arrow rotates left
if (key[KEY_LEFT])
{
angle -= 0.1;
if (angle < 0) angle = 256;
rotate_sprite(screen, tank, x, y, itofix(angle));
}
//right arrow rotates right
if (key[KEY_RIGHT])
{
angle += 0.1;
if (angle > 256) angle = 0;
rotate_sprite(screen, tank, x, y, itofix(angle));
}
//display angle
textprintf_ex(screen, font, 0, 10, WHITE, 0,
"Angle = %f", angle);
}
}
allegro_exit();
return 0;
}
END_OF_MAIN()
the program triggering the breakpoint is: crt0msg.c off of the disk.
snippet of code:
#ifdef _DEBUG
/*
* Report error.
*
* If _CRT_ERROR has _CRTDBG_REPORT_WNDW on, and user chooses
* "Retry", call the debugger.
*
* Otherwise, continue execution.
*
*/
if (rterrnum!=_RT_CRNL&&rterrnum!=_RT_BANNER&&rterrnum!=_RT_CRT_NOTINIT)
{
if (1 == _CrtDbgReport(_CRT_ERROR, NULL, 0, NULL,rterrs[tblindx].rterrtxt))
_CrtDbgBreak();
}
#endif /* _DEBUG */

tank = load_bitmap("C:\Users\Jason\Desktop\module7\tank.bmp", NULL);
Your compiler should be warning you about that string since it contains invalid escaped characters. You should use double back slashes or single forward slashes:
tank = load_bitmap("C:\\Users\\Jason\\Desktop\\module7\\tank.bmp", NULL);
// or
tank = load_bitmap("C:/Users/Jason/Desktop/module7/tank.bmp", NULL);
The latter format is recommended because it is cross platform. (Minus the whole bit about hard coding an absolute path.)
Finally, you really need to check return codes:
if (!tank) {
// gracefully report error and exit
}
Otherwise the program will crash somewhere else and it will be harder to debug.

Related

Missing closing brace in SFML program?

I made a program using C++ and SFML. The program is supposed to generate 20 circles that are either red or blue, and it did work. Yet, I made a few changes, saved, and came back to it on VS a few hours later to find that I keep getting an error:
'{': No matching token found (Line 9)
I keep scanning through the code and I can't seem to find the issue at all.
Code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <chrono>
#include <random>
using namespace std;
int main()
{ //Line 9
unsigned seed = chrono::system_clock::now().time_since_epoch().count();
default_random_engine generator(seed);
uniform_int_distribution<int> distribution1(0, 1024);
uniform_int_distribution<int> distribution2(1, 2);
sf::RenderWindow window(sf::VideoMode(1024, 1024), "Spooky Circle Box");
sf::CircleShape shape(100.f);
shape.setFillColor(sf::Color::Red);
shape.setPosition(10, 10);
std::vector<sf::CircleShape> circles(20);
window.clear();
for (unsigned int i = 0; i < circles.size(); i++) {
int find = 0;
int find_color = 0;
while (find != 20) {
circles[i].setPosition(distribution1(generator), distribution1(generator));
for (unsigned int j = 0; j < circles.size(); j++) {
if (i == j || (circles[i].getPosition().x != circles[j].getPosition().x || circles[i].getPosition().y != circles[j].getPosition().y)) {
find++;
} else;
if (find != 20) {
find = 0;
} else;
}
find = 0;
find_color = distribution2(generator);
circles[i].setRadius(5.f);
if (find_color == 1) {
circles[i].setFillColor(sf::Color::Blue);
} else { circles[i].setFillColor(sf::Color::Red); }
window.draw(circles[i]);
}
window.display();
while (window.isOpen()) {
sf::sleep((sf::milliseconds(100)));
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
}
return 0;
}
I explain further my comments, but I'm not going to post any repaired code. I only suggest a way of doing things.
By the structure of you're code, it seems you're trying to generate some blue or red circles randomly distributed over the window, but, at the same time, you're trying to draw them.
You should differentiate your actual data from your drawing stuff. My suggested pseudo-code would be.
int main(){int main(){
// 1 . Declare your circle vector
// 2 . Populate that vector with random circles (random position, random color)
// Now draw those circles
// 3 . while(window.isOpen()) loop
// 3.1 Clear the window
// 3.2 Draw your circles
// 3.3 Display the stuff
}
That point 3 it's basically the way to draw stuff acording SFML tutorials.

Why is the mouse movement not captured in the openGL program developed in Linux?

I am currently moving my opengl codes I developed in Windows/Mac to Ubuntu. I don't know if my Ubuntu has a really slow performance, or if my code is wrong. When the render window is open, the camera system should read the mouse movement and change the yaw/pitch/fork of the camera, but it just reads the keyboard input, and not the mouse movement. I can only move around the camera with the keyboard.
Here are the basic codes regarding the mouse callback.
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", glfwGetPrimaryMonitor(), NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
....
}
Here is the mouse_callback() function I send to glfwSetCursorPosCallback()
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
Is it my slow Ubuntu problem or the code problem? This code even works in Mac, which is considered a really similar OS with Linux. I'm currently using the glfw3 library.
Here is the code related to the input keyboard processing, which works really well. (Just in case)
Inside my render loop
while (!glfwWindowShouldClose(window))
{
// per-frame time logic
// --------------------
VISITED = new unsigned int[11]{ 0,0,0,0,0,0,0,0,0,0,0 };
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// input
// -----
processInput(window);
//render
.......
glfwSwapBuffers(window);
glfwPollEvents();
}
My processInput() function
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
}
EDIT
I tried std::cout << "callback called" <<std::endl in the mouse_callback() function. It is called, but only maximum 2 times in the whole program. It's either called once or twice only when the program starts. Maybe it is a bug of glfw in Ubuntu

How to load windows cursors (.cur) in Linux using XLib and Xcursors?

I'm trying to load a .cur file via Xcursor library and i just can't make it work. I'm working with linux (Bodhi + Lubuntu) on virtualbox. Here's my code:
my includes
// GLFW
#define GLFW_DLL // use the GLFW .dll
#include "gl/GLFW/glfw3.h"
...
#if defined(__linux__)
// GLFW native expoose
#define GLFW_EXPOSE_NATIVE_X11 // expose X11 and GLX context (linux)
#define GLFW_EXPOSE_NATIVE_GLX
#include "gl/GLFW/glfw3native.h" // for low-level expose
// X Window
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xcursor/Xcursor.h>
#endif
in program, after GLFW window creation
#if defined(__linux__)
Display* dpy = glfwGetX11Display();
Window xwindow = glfwGetX11Window(window);
Cursor crs = XcursorFilenameLoadCursor(dpy,"somefolder/default.cur");
XDefineCursor(dpy,xwindow,crs);
#endif
...
#if defined(__linux__)
XFreeCursor(dpy,crs);
#endif
Any obvious error? I can't really find sources about Xcursor and i'm close to switch to typical OpenGL textures and get over it.
NOTE: I'm already disabled Virtualbox integrated mouse, so the mouse is entirely on guest machine and i run it fullscreen.
Since i solved my problem i decided it's a good idea to post my results, in hope of helping someone trying to make his application more cross-platform.
My code follows, and explanation follows the code.
ESSENTIAL
X11 development headers package (etc. libx11-dev)
Xcursor development headers package (etc. libxcursor-dev)
A portable <stdint.h> header, like this one.
CODE
...
// to store colors
struct COLOR {
uint8_t r,g,b,a;
COLOR() {
this->r = this->g = this->b = this->a = 0;
}
COLOR(uint8_t r,uint8_t g,uint8_t b) {
this->r = r;
this->g = g;
this->b = b;
this->a = 255;
}
bool operator == (COLOR c) const {
return (r == c.r && g == c.g && b == c.b);
}
};
size_t get_bit(int32_t number,int8_t position) {
size_t bitmask = 1 << position;
return (number & bitmask) ? 1 : 0;
}
....
// load cursor
#if defined(_WIN32)
// etc. use standard WinApi code, (HCURSOR)LoadImage(..) and such
#endif
#if defined(__linux__)
Display* display = XOpenDisplay(NULL);
string filename = "mycursor.cur"; // <-- add your .cur cursor here
char buf[4];
FILE* fp = fopen(filename.c_str(),"rb");
fread(buf,1,2,fp); // reserved. always 0
fread(buf,1,2,fp); // image type; we're only interested for .cur (=2)
int16_t imagetype = buf[0] | (buf[1]<<8);
if (imagetype!=2) {
// error: file is not a valid .cur file
return;
}
fread(buf,1,2,fp); // number of images
// we're only interested in the first image
fread(buf,1,1,fp); // width (0 to 255; 0=means 256 width)
int8_t width = (buf[0]==0 ? 256 : buf[0]);
fread(buf,1,1,fp); // height (0 to 255; 0=means 256 height)
int8_t height = (buf[0]==0 ? 256 : buf[0]);
fread(buf,1,1,fp); // number of colors in palette (0 for no palette)
fread(buf,1,1,fp); // reserved. should be 0
fread(buf,1,2,fp); // hotspot x
int16_t hotspot_x = buf[0] | (buf[1]<<8);
fread(buf,1,2,fp); // hotspot y
int16_t hotspot_y = buf[0] | (buf[1]<<8);
fread(buf,1,4,fp); // image data in bytes
fread(buf,1,4,fp); // offset to image data
// Now we need to verify if image in .cur is BMP or PNG (Vista+)
// We can't just check 'magic' since if it's BMP, the header will be missing (PNG is whole)
// So we search for PNG magic; if doesnt exist, we have a BMP!
// NOTE: for simplicity we go only for BMP for the moment.
// So just check if 'biSize' is 40 (Windows NT & 3.1x or later)
// BITMAPINFOHEADER
fread(buf,1,4,fp); // biSize
int32_t biSize = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
if (biSize!=40) {
// error: file does not contain valid BMP data;
return;
}
fread(buf,1,4,fp); // biWidth
int32_t biWidth = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
fread(buf,1,4,fp); // biHeight (if positive => bottom-up, if negative => up-bottom)
int32_t biHeight = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
fread(buf,1,2,fp); // biPlanes
fread(buf,1,2,fp); // biBitCount
int16_t biBitCount = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
if (biBitCount!=24 && biBitCount!=32) {
// error: only 24/32 bits supported;
return;
}
fread(buf,1,4,fp); // biCompression
int32_t biCompression = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
// we want only uncompressed BMP data
if (biCompression!=0 /*BI_RGB*/ ) {
// error: file is compressed; only uncompressed BMP is supported;
return;
}
fread(buf,1,4,fp); // biSizeImage
fread(buf,1,4,fp); // biXPelsPerMeter
fread(buf,1,4,fp); // biYPelsPerMeter
fread(buf,1,4,fp); // biClrUsed
fread(buf,1,4,fp); // biClrImportant
// DECODE IMAGE
uint8_t origin = (biHeight>0 ? 0 : 1); // 0=bottom-up, 1=up-bottom
// there are cases where BMP sizes are NOT the same with cursor; we use the cursor ones
biWidth = width;
biHeight = height;
COLOR* pixels = new COLOR[biWidth * biHeight];
for(int32_t y=0;y<biHeight;y++) {
for(int32_t x=0;x<biWidth;x++) {
uint32_t offset = ((origin==1?y:biHeight-1-y)*biWidth)+x;
// read pixels by number of bits
switch(biBitCount) {
// 24-bit
case 24:
fread(buf,1,3,fp);
pixels[offset] = COLOR(buf[0],buf[1],buf[2]);
break;
// 32-bit
case 32:
fread(buf,1,4,fp);
pixels[offset] = COLOR(buf[0],buf[1],buf[2]);
pixels[offset].a = buf[3];
break;
}
}
}
// read mask
// mask is 1-bit-per-pixel for describing the cursor bitmap's opaque and transparent pixels.
// so for 1 pixel we need 1 bit, for etc. 32 pixels width, we need 32*1 bits (or 4 bytes per line)
// so for etc. 32 pixels height we need 4*32 = 128 bytes or [mask bytes per line * height]
uint16_t mask_bytes_per_line = biWidth / 8;
uint16_t mask_bytes = mask_bytes_per_line * biHeight;
char* mask = new char[mask_bytes];
fread(mask,1,mask_bytes,fp);
fclose(fp);
// reverse every [mask_bytes_per_line] bytes; (etc. for 4 bytes per line do: 0,1,2,3 => 3,2,1,0) -> you really need to ask Microsoft for this 8)
char rmask[4];
for(uint16_t x=0;x<mask_bytes;x+=mask_bytes_per_line) {
for(uint16_t r=0;r<mask_bytes_per_line;r++) {
rmask[r] = mask[x+mask_bytes_per_line-1-r];
}
// copy the reversed line
for(uint16_t r=0;r<mask_bytes_per_line;r++) {
mask[x+r] = rmask[r];
}
}
// now extract all bits from mask bytes into new array (equal to width*height)
vector<uint8_t> bits;
for(uint16_t x=0;x<mask_bytes;x++) {
for(uint8_t b=0;b<8;b++) {
bits.push_back( get_bit(mask[x],b) );
}
}
delete[] mask;
// reverse vector (?)
reverse(bits.begin(),bits.end());
// now the bits contains =1 (transparent) and =0 (opaque) which we use on cursor directly
XcursorImage* cimg = XcursorImageCreate(biWidth,biHeight);
cimg->xhot = hotspot_x;
cimg->yhot = hotspot_y;
// create the image
for(int32_t y=0;y<biHeight;y++) {
for(int32_t x=0;x<biWidth;x++) {
uint32_t offset = (y*biWidth)+x;
COLOR pix = pixels[offset];
cimg->pixels[offset] = ((bits[offset]==1?0:pix.a)<<24) + (pix.r<<16) + (pix.g<<8) + (pix.b);
}
}
// create cursor from image and release the image
Cursor cursor = XcursorImageLoadCursor(display,cimg);
XcursorImageDestroy(cimg);
...
// set the cursor
XDefineCursor(display,yourwindow,cursor);
XFlush(display);
...
// free cursor
if (cursor!=None) {
XFreeCursor(display,cursor);
}
The above code takes a Windows .cur cursor file and creates an Xcursor for use in X11 window system. Of course, there are lots of limitations to my .cur format handling but one can easily add his own improvements to the above code (like say supporting 8-bit cursors). The above code not only take care of alpha-transparency but also 32-bit alpha-blended cursors

C++ First-chance exception

I have wrote a shape detection code with c++.
I am using Visual Studio 2013 Express Desktop Edition.
When I run the program it will give following error.
First-chance exception at 0x54EE3C77 (opencv_imgproc244d.dll) in Final.exe: 0xC0000005: Access violation reading location 0x05958000.
If there is a handler for this exception, the program may be safely continued.
Following is the code sample.
#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <cv.h>
#include <highgui.h>
#include <windows.h>
void shapeDetectionNew(IplImage* img)
{
try
{
//converting the original image into grayscale
IplImage* imgGrayScale = cvCreateImage(cvGetSize(img), 8, 1);
cvCvtColor(img, imgGrayScale, CV_BGR2GRAY);
//thresholding the grayscale image to get better results
cvThreshold(imgGrayScale, imgGrayScale, 128, 255, CV_THRESH_BINARY);
CvSeq* contour; //hold the pointer to a contour
CvSeq* result; //hold sequence of points of a contour
CvMemStorage *storage = cvCreateMemStorage(0); //storage area for all contours
//finding all contours in the image
cvFindContours(imgGrayScale, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
//***********************************
cv::Mat img = cv::imread("F:\\My Works\\Opencv\\Shape_Detection_Images\\shape.jpg");
//convert IplImage to Mat
//cv::Mat img(iplImg);
cv::Mat gray;
cv::cvtColor(img, gray, CV_BGR2GRAY);
// Use Canny instead of threshold to catch squares with gradient shading
cv::Mat bw;
cv::Canny(gray, bw, 0, 50, 5);
// Find contours
std::vector<std::vector<cv::Point> > contours;
cv::findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> approx;
cv::Mat dst = img.clone();
//***********************************
for (int i = 0; i < contours.size(); i++)
{
// Approximate contour with accuracy proportional
// to the contour perimeter
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
// Skip small or non-convex objects
if (std::fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
if (approx.size() == 3)
{
setLabel(dst, "TRI", contours[i]); // Triangles
}
else if (approx.size() >= 4 && approx.size() <= 6)
{
// Number of vertices of polygonal curve
int vtc = approx.size();
// Get the cosines of all corners
std::vector<double> cos;
for (int j = 2; j < vtc + 1; j++)
cos.push_back(angle(approx[j%vtc], approx[j - 2], approx[j - 1]));
// Sort ascending the cosine values
std::sort(cos.begin(), cos.end());
// Get the lowest and the highest cosine
double mincos = cos.front();
double maxcos = cos.back();
// Use the degrees obtained above and the number of vertices
// to determine the shape of the contour
if (vtc == 4 && mincos >= -0.1 && maxcos <= 0.3)
setLabel(dst, "RECT", contours[i]);
else if (vtc == 5 && mincos >= -0.34 && maxcos <= -0.27)
setLabel(dst, "PENTA", contours[i]);
else if (vtc == 6 && mincos >= -0.55 && maxcos <= -0.45)
setLabel(dst, "HEXA", contours[i]);
}
else
{
// Detect and label circles
double area = cv::contourArea(contours[i]);
cv::Rect r = cv::boundingRect(contours[i]);
int radius = r.width / 2;
if (std::abs(1 - ((double)r.width / r.height)) <= 0.2 &&
std::abs(1 - (area / (CV_PI * std::pow((double)radius, 2)))) <= 0.2)
setLabel(dst, "CIR", contours[i]);
}
}
cv::imshow("Shape_Detection", dst);
}
catch (int e)
{
throw e;
}
}
int main()
{
try
{
// Create CvCapture object to grab data from the webcam
CvCapture* pCapture;
// Start capturing data from the webcam
pCapture = cvCaptureFromCAM(1);
// Define the IplImage pointers we're going to use as globals
IplImage* pFrame;
IplImage* pProcessedFrame;
IplImage* tempFrame;
pFrame = cvQueryFrame(pCapture);
//Create the low threshold slider
// Format: Slider name, window name, reference to variable for slider, max value of slider, callback function
cvCreateTrackbar("Low Threshold", "Edge_Detection", &lowSliderPosition, maxLowThreshold, onLowThresholdSlide);
// Create the high threshold slider
cvCreateTrackbar("High Threshold", "Edge_Detection", &highSliderPosition, maxHighThreshold, onHighThresholdSlide);
// Create a greyscale image which is the size of our captured image
pProcessedFrame = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U, 1);
// Create a frame to use as our temporary copy of the current frame but in grayscale mode
tempFrame = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U, 1);
char keypress;
bool quit = false;
int counterCheck = 1;
while (quit == false)
{
// Make an image from the raw capture data
// Note: cvQueryFrame is a combination of cvGrabFrame and cvRetrieveFrame
pFrame = cvQueryFrame(pCapture);
// Draw the original frame in our window
cvShowImage("Live_Cam", pFrame);
shapeDetectionNew(pFrame);
} // End of while loop
cvDestroyAllWindows();
}
catch (Exception ex)
{
}
return 1;
}
You are not checking that the result of
pFrame = cvQueryFrame(pCapture);
is not null. If it is, eventuallu this will get passed to cvCvtColor() and hence the exception.
Completely separate problem:imshow() will not display anything without a subsequent call to waitKey()

Multithreaded game program suddenly locks up on glXSwapBuffers

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
}
}
}
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.
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).

Resources