General questions about Bullet performance.

Nuclaer
Posts: 2
Joined: Wed Mar 28, 2012 4:42 am

General questions about Bullet performance.

Post by Nuclaer »

Hello,
I have been doing some testing with OpenGL and Bullet and I am wondering about some limitations. I have a simple scene set up on which I render X amount of boxes and compare frame rates. I am able to push about 200-400 boxes (all colliding with each other simultaneously) at reasonable FPS. I saw the blog post about some guy pushing 110k objects in real time. I am wondering how on earth this is possible and what the maximum number of simple shaped objects that a common video card/CPU can handle at once. Is their source code available for the 110k project (http://bulletphysics.org/wordpress/?p=340)? I am not sure if my novice coding is why I can only handle a couple hundred objects or if it is a hardware/computing limitation.
My PC has decent specs (640 GTX, 4.0GHz CPU).
Thanks.
MaxDZ8
Posts: 149
Joined: Fri Jun 24, 2011 8:53 am

Re: General questions about Bullet performance.

Post by MaxDZ8 »

1st fact: GPUs are embarassingly good at massively parallel tasks. CUDA and GPGPU physics have been in the works for a while: I suppose this is beginning to reap the benefits.
2nd: I seriously hope you can move far more than 200 objects, I used to move around 100 on a fairly old machine. Are you referring to the bullet tests? If not, you might have to look at your GL code.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: General questions about Bullet performance.

Post by Erwin Coumans »

The work-in-progress GPU/OpenCL rigid body pipeline source code is available in the Extras folder of the latest Bullet 2.80 version. Note that the code is separate and not integrated in the regular/CPU Bullet version. A lot of effort has been made to improve the performance of the GPU/OpenCL rigid body pipeline. All the data stays on the GPU and rendering is done using instancing.

The regular Bullet version should be able to simulate a lot more than 400 rigid bodies. Are you using an unoptimized/debug build?
Nuclaer
Posts: 2
Joined: Wed Mar 28, 2012 4:42 am

Re: General questions about Bullet performance.

Post by Nuclaer »

Thanks for your help guys.
I am running the normal release build which I compiled using the VS2010 project file. My problem is in how I am allocating data (I think). I can render 1000 objects at once on screen as long as they are not colliding with each other, once they do, my frame drops drastically to like 1-2. Here is my source code, sorry its a bit messy :P. Basically I am initializing a number of PhysicsObjects (class) then adding all the rigid bodies to a world. Then I draw them all by grabbing the opening matrix and drawing a VBO. I am trying not to use any outdated OpenGL stuff.
PS. I know this isn't an OpenGL forum, the Bullet stuff is in the class and the main Init function. Also, I understand if if this is to homework-ish to be answered so no worries.
Thanks.

Code: Select all

#include "PhysicsObject.h"

PhysicsObject::PhysicsObject()
{
	
}


PhysicsObject::~PhysicsObject(void)
{
	delete [] rigidBody;
	delete [] colShape;
}

void PhysicsObject::Init(btCollisionShape* cS) {
	colShape = cS;
	btScalar mass = 1;
	
	btDefaultMotionState* mS = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)));
	btVector3 fallInertia(0,0,0);
	colShape->calculateLocalInertia(mass,fallInertia);
	btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(mass,mS,cS,fallInertia);
	rigidBody = new btRigidBody(rigidBodyCI);
}

bool PhysicsObject::SetPosition(glm::vec3 nPos)
{
	if (rigidBody != NULL) {
		btTransform nTrans;
		rigidBody->getMotionState()->getWorldTransform(nTrans);
		nTrans.setOrigin(btVector3(nPos.x, nPos.y, nPos.z));
		delete rigidBody->getMotionState();
		rigidBody->setMotionState(new btDefaultMotionState(nTrans));
		return true;
	}

	return false;
}

bool PhysicsObject::SetRotation(glm::vec4)
{
	return true;
}

bool PhysicsObject::SetVelocity(glm::vec3 nVelocity)
{
	rigidBody->setLinearVelocity(btVector3(nVelocity.x, nVelocity.y, nVelocity.z));
	return true;
}

bool PhysicsObject::SetMass(float nMass)
{
	rigidBody->setMassProps(nMass, btVector3(0,0,0));
	rigidBody->updateInertiaTensor();
}

Code: Select all

#pragma once

#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\matrix_inverse.hpp>
#include <glm\gtc\type_ptr.hpp>

class PhysicsObject
{
public:
	PhysicsObject(void);
	~PhysicsObject(void);

	btRigidBody *rigidBody;
	btCollisionShape *colShape;

	void Init(btCollisionShape*);
	bool SetPosition(glm::vec3);
	bool SetRotation(glm::vec4);
	bool SetVelocity(glm::vec3);
	bool SetMass(float);

};

Code: Select all

#include <GL\glew.h>
#include <Windows.h>
#include <iostream>
#include <string.h>
#include <fstream>
#include <math.h>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\matrix_inverse.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <GL\glfw.h>
#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
#include <stack>

#include "ShaderHandler.h"
#include "PhysicsObject.h"

using namespace std;

#define PI 3.14159265
int count = 0;
float screenWidth = 800;
float screenHeight = 800;
int screenMidX = screenWidth/2;
int screenMidY = screenHeight/2;

GLuint vaoID; // Our Vertex Array Object  
GLuint vboID; // Our Vertex Buffer Object 
GLuint ivboID;
ShaderHandler *shader;

double multi;

bool forwardDown = false;
bool backwardDown = false;
bool leftDown = false;
bool rightDown = false;
bool spacebarDown = false;

glm::vec2 mousePos;


double timeDelta = 0.0;
double timeSinceUpdate = 0.0;
double debugTime = 0.0;

PhysicsObject *boxes;

GLuint vertexShader, fragShader;

GLfloat rot;
GLfloat movementSpeed = .80f;
GLfloat rotationSpeed = 2;

std::stack<glm::mat4> modelMatrix;
glm::mat4 projectionMatrix(1.0);
glm::mat4 viewMatrix(1.0);
glm::mat4 MVP(1.0);

glm::vec3 cameraAngle;
glm::vec3 cameraPosition;

GLfloat lightPos[] = {0.0, 3.0, -10.0, 1.0};
GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0}; // Farbdefinitionen
GLfloat diffuse[] = {0.9, 0.9, 0.9, 1.0};
GLfloat specular[] = {1.0, 1.0, 1.0, 1.0};

long int frames;
GLdouble t;

int l = 6;
int w = 100;
int h = 1;

struct Vertex
{
	float x, y, z;
	float padding[13];
};

btDiscreteDynamicsWorld* dynamicsWorld;
btRigidBody* fallRigidBody;


void RenderScene(void) {
	frames++;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glLoadIdentity();

	modelMatrix.push(glm::mat4(1.0));

	viewMatrix = glm::mat4(1.0f); 

	viewMatrix = glm::rotate(viewMatrix, cameraAngle.x, glm::vec3(1.0f, 0.0f, 0.0f));
	viewMatrix = glm::rotate(viewMatrix, cameraAngle.y, glm::vec3(0.0f, 1.0f, 0.0f));
	viewMatrix = glm::rotate(viewMatrix, cameraAngle.z, glm::vec3(0.0f, 0.0f, 0.1f));
	viewMatrix = glm::translate(viewMatrix, glm::vec3(cameraPosition.x, cameraPosition.y, cameraPosition.z));

	projectionMatrix = glm::perspective(30.0f, screenWidth / screenHeight, 1.0f, 5000.0f);

	MVP = projectionMatrix * viewMatrix * modelMatrix.top();

	glUniformMatrix4fv(glGetUniformLocation(shader->shaderProgram, "MVP"), 1, GL_FALSE, glm::value_ptr(MVP));

	//glm::mat4 uMVP = glm::inverse(MVP);
	//glUniformMatrix4fv(glGetUniformLocation(shader->shaderProgram, "uMVP"), 1, GL_FALSE, glm::value_ptr(uMVP));	

glm::mat4 tempMat(1.0);

	tempMat = glm::translate(tempMat, glm::vec3(0.0, 0.0, 0.0));
	modelMatrix.push(tempMat);
	glBegin(GL_LINES);
	for (float i = 0; i < 300.0; i+=5.0) {
		glVertex3f(i,  0.0f, 0.0);
		glVertex3f(i, 0.0f, -300.0f);
	}

	for (float i = 0; i < 300.0; i+=5.0) {
		glVertex3f(0.0,  0.0f, -i);
		glVertex3f(300.0f, 0.0f, -i);
	}
	glEnd();
	modelMatrix.pop();
	
	tempMat = glm::translate(tempMat, glm::vec3(5.0, 0.0, 20.0));

	modelMatrix.push(tempMat);
	MVP = projectionMatrix * viewMatrix * modelMatrix.top();
	glUniformMatrix4fv(glGetUniformLocation(shader->shaderProgram, "MVP"), 1, GL_FALSE, glm::value_ptr(MVP));	
	


	glBindVertexArray(vaoID); // Bind our Vertex Array Object  

	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, (void*)ivboID); // Draw our square  
	glBindVertexArray(0); // Unbind our Vertex Array Object
	modelMatrix.pop();

	btTransform trans;

	glBindVertexArray(vaoID); // Bind our Vertex Array Object  
	for (int i = 0; i < l*w*h; i++) {
			boxes[i].rigidBody->getMotionState()->getWorldTransform(trans);

			trans.getOpenGLMatrix(glm::value_ptr(tempMat));
			modelMatrix.push(tempMat);
			MVP = projectionMatrix * viewMatrix * modelMatrix.top();
			glUniformMatrix4fv(glGetUniformLocation(shader->shaderProgram, "MVP"), 1, GL_FALSE, glm::value_ptr(MVP));	
			

			glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, (void*)ivboID); // Draw our square  
			
			modelMatrix.pop();

	}
	glBindVertexArray(0); // Unbind our Vertex Array Object
}

void ResizeWindow(int nWidth, int nHeight)
{
	glViewport(0, 0, nWidth, nHeight);
	screenWidth = nWidth;
	screenHeight = nHeight;
}

void CreateSquare(void) 
{
	float size = 1;

	GLfloat vertices[24] = {
		-size, -size, -size,
		size, -size, -size,
		size, size, -size,
		-size, size, -size,

		-size, -size, size,
		size, -size, size,
		size, size, size,
		-size, size, size,
	};

	GLuint indices[24] = {
		0, 1, 2, 3,                 // Front face
		4, 5, 6, 7,                 // Back face
		0, 3, 7, 4,                 // Left face
		1, 2, 6, 5,                 // Right face
		2, 3, 7, 6,                 // Top face
		0, 1, 5, 4,                 // Bottom face
	};



	glGenVertexArrays(1, &vaoID); // Create our Vertex Array Object  
	glBindVertexArray(vaoID); // Bind our Vertex Array Object so we can use it  


	glGenBuffers(1, &vboID); // Generate our Vertex Buffer Object  
	glBindBuffer(GL_ARRAY_BUFFER, vboID); // Bind our Vertex Buffer Object 
	glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW  

	glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Set up our vertex attributes pointer  

	glGenBuffers(1, &ivboID);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ivboID); // Bind our Vertex Buffer Object  
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, 24 * sizeof(GLuint), indices, GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW  



	glEnableVertexAttribArray(0); // Disable our Vertex Array Object  
	glBindVertexArray(0); // Disable our Vertex Buffer Object  
}

void Initialize(void)
{
	glMatrixMode(GL_MODELVIEW);
	glEnable(GL_DEPTH_TEST);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glClearColor(0.0f, 1.0f, 0.0f, 1.0f);

	cameraPosition.y = -5.0;

	glShadeModel (GL_SMOOTH); //set the shader to smooth shader
	ResizeWindow(screenWidth, screenHeight);
	CreateSquare();


	shader = new ShaderHandler("shader.v", "frag.f", true);
	glUseProgram(shader->shaderProgram);

	btBroadphaseInterface*											broadphase =				new btDbvtBroadphase();
	btDefaultCollisionConfiguration*								collisionConfiguration =	new btDefaultCollisionConfiguration();
	btCollisionDispatcher*											dispatcher =				new btCollisionDispatcher(collisionConfiguration);
	btSequentialImpulseConstraintSolver*							solver =					new btSequentialImpulseConstraintSolver;
	dynamicsWorld =				                                                                new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);

	dynamicsWorld->setGravity(btVector3(0,-9.8,0));

	btScalar mass = 1.0;

	btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0, 1, 0),0.0);

	btCollisionShape* fallShape = new  btBoxShape(btVector3(1.0, 1.0, 1.0));
	btDefaultMotionState* mS = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1),btVector3(0, 0, 0)));
	

	btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)));
	btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
	btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
	dynamicsWorld->addRigidBody(groundRigidBody);

	
	boxes = new PhysicsObject[l*w*h];
	for (int i = 0; i < l*w*h; i++)
	{
		boxes[i].Init(fallShape);
	}
	
	for (int x = 0; x < l; x++) {
		for (int y = 0; y < w; y++) {
			for (int z = 0; z < h; z++) {
				boxes[count].SetPosition(glm::vec3(x*5, y*2+1.5, -z*5));
				count++;
			}
		}
	}

	for (int i = 0; i < (l*w*h); i++) {
		dynamicsWorld->addRigidBody(boxes[i].rigidBody);
	}
}

void KeyboardInput(int key, int action)
{
	if (action == GLFW_PRESS) {
		if (key == 87) {
			forwardDown = true;
		}
		if (key == 83) {
			backwardDown = true;
		} 
		if (key == 65) {
			leftDown = true;
		} 
		if (key == 68) {
			rightDown = true;
		} 
		if (key == 74) {
			boxes[0].SetPosition(-cameraPosition);
			boxes[0].SetVelocity(glm::vec3(0.0, 0.0, -100.0));
		} 


	} else {
		if (key == 87) {
			forwardDown = false;
		}
		if (key == 83) {
			backwardDown = false;
		}
		if (key == 65) {
			leftDown = false;
		}
		if (key == 68) {
			rightDown = false;
		}
	}
}

void MouseInput(int x, int y)
{
	int xDif = screenMidX - x;
	int yDif = screenMidY - y;


	cameraAngle.x -= (double)yDif/9.0;
	cameraAngle.y -= (double)xDif/9.0;
}

void Update(double time)
{
	dynamicsWorld->stepSimulation(time,10);
	t+= time;

	if (debugTime > 1) {
		printf("%lf \n", frames/t);
		frames = 0;
		t = 0;
		debugTime = 0.0;


	}

	debugTime += t;
	multi = (60/(1/time));
	rot+=1*multi;

	if (forwardDown) {
		cameraPosition.z += cos(cameraAngle.y * (PI/180))*movementSpeed*multi;
		cameraPosition.x += -sin(cameraAngle.y * (PI/180))*movementSpeed*multi;
	}
	if (backwardDown) {
		cameraPosition.z -= cos(cameraAngle.y * (PI/180))*movementSpeed*multi;
		cameraPosition.x -= -sin(cameraAngle.y * (PI/180))*movementSpeed*multi;
	}
	if (leftDown) {
		cameraPosition.x += cos((cameraAngle.y) * (PI/180))*movementSpeed*multi;
		cameraPosition.z += sin((cameraAngle.y + (PI/2)) * (PI/180))*movementSpeed*multi;
	}
	if (rightDown) {
		cameraPosition.x += -cos(cameraAngle.y * (PI/180))*movementSpeed*multi;
		cameraPosition.z += -sin((cameraAngle.y + (PI/2)) * (PI/180))*movementSpeed*multi;
	}

	glfwGetMousePos((int*)&mousePos.x, (int*)&mousePos.y);
}

int main(int argc, char* argv[])
{
	int running = GL_TRUE;
	double startTime = 0.0;

	glfwInit();
	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4);
	glfwOpenWindow(screenWidth, screenHeight, 0,0,0,0,0,0, GLFW_WINDOW );
	glfwSetWindowPos(900, 200);

	if (GLEW_OK != glewInit())
	{
		// GLEW failed!
		exit(1);
	}

	glfwSetWindowSizeCallback(ResizeWindow);            

	glfwSetKeyCallback(KeyboardInput);
	glfwSetMousePosCallback(MouseInput);

	glfwDisable(GLFW_MOUSE_CURSOR);


	Initialize();

	startTime = glfwGetTime();
	glfwSetTime(0.0);

	//glfwSwapInterval(1);

	// Main loop
	while( running )
	{
		glfwSetMousePos(screenWidth/2, screenHeight/2);
		timeDelta = glfwGetTime() - startTime;
		startTime = glfwGetTime();

		// OpenGL rendering goes here...
		glClear( GL_COLOR_BUFFER_BIT );
		// Swap front and back rendering buffers
		Update(timeDelta);
		RenderScene();
		glfwSwapBuffers();


		// Check if ESC key was pressed or window was closed
		running = !glfwGetKey( GLFW_KEY_ESC ) &&
			glfwGetWindowParam( GLFW_OPENED );
	}

	delete dynamicsWorld;

	glfwTerminate();
	return 0;
}