mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2024-11-25 13:29:17 +08:00
746 lines
19 KiB
C++
746 lines
19 KiB
C++
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
Particle system example with collisions using uniform grid
|
|
|
|
CUDA 2.1 SDK release 12/2008
|
|
- removed atomic grid method, some optimization, added demo mode.
|
|
|
|
CUDA 2.2 release 3/2009
|
|
- replaced sort function with latest radix sort, now disables v-sync.
|
|
- added support for automated testing and comparison to a reference value.
|
|
*/
|
|
|
|
// OpenGL Graphics includes
|
|
#include <helper_gl.h>
|
|
#if defined(WIN32)
|
|
#include <GL/wglew.h>
|
|
#endif
|
|
#if defined(__APPLE__) || defined(__MACOSX)
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
#include <GLUT/glut.h>
|
|
#ifndef glutCloseFunc
|
|
#define glutCloseFunc glutWMCloseFunc
|
|
#endif
|
|
#else
|
|
#include <GL/freeglut.h>
|
|
#endif
|
|
|
|
// CUDA runtime
|
|
#include <cuda_runtime.h>
|
|
|
|
// CUDA utilities and system includes
|
|
#include <helper_functions.h>
|
|
#include <helper_cuda.h> // includes cuda.h and cuda_runtime_api.h
|
|
|
|
// Includes
|
|
#include <stdlib.h>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <algorithm>
|
|
|
|
#include "particleSystem.h"
|
|
#include "render_particles.h"
|
|
#include "paramgl.h"
|
|
|
|
#define MAX_EPSILON_ERROR 5.00f
|
|
#define THRESHOLD 0.30f
|
|
|
|
#define GRID_SIZE 64
|
|
#define NUM_PARTICLES 16384
|
|
|
|
const uint width = 640, height = 480;
|
|
|
|
// view params
|
|
int ox, oy;
|
|
int buttonState = 0;
|
|
float camera_trans[] = {0, 0, -3};
|
|
float camera_rot[] = {0, 0, 0};
|
|
float camera_trans_lag[] = {0, 0, -3};
|
|
float camera_rot_lag[] = {0, 0, 0};
|
|
const float inertia = 0.1f;
|
|
ParticleRenderer::DisplayMode displayMode = ParticleRenderer::PARTICLE_SPHERES;
|
|
|
|
int mode = 0;
|
|
bool displayEnabled = true;
|
|
bool bPause = false;
|
|
bool displaySliders = false;
|
|
bool wireframe = false;
|
|
bool demoMode = false;
|
|
int idleCounter = 0;
|
|
int demoCounter = 0;
|
|
const int idleDelay = 2000;
|
|
|
|
enum { M_VIEW = 0, M_MOVE };
|
|
|
|
uint numParticles = 0;
|
|
uint3 gridSize;
|
|
int numIterations = 0; // run until exit
|
|
|
|
// simulation parameters
|
|
float timestep = 0.5f;
|
|
float damping = 1.0f;
|
|
float gravity = 0.0003f;
|
|
int iterations = 1;
|
|
int ballr = 10;
|
|
|
|
float collideSpring = 0.5f;
|
|
;
|
|
float collideDamping = 0.02f;
|
|
;
|
|
float collideShear = 0.1f;
|
|
float collideAttraction = 0.0f;
|
|
|
|
ParticleSystem *psystem = 0;
|
|
|
|
// fps
|
|
static int fpsCount = 0;
|
|
static int fpsLimit = 1;
|
|
StopWatchInterface *timer = NULL;
|
|
|
|
ParticleRenderer *renderer = 0;
|
|
|
|
float modelView[16];
|
|
|
|
ParamListGL *params;
|
|
|
|
// Auto-Verification Code
|
|
const int frameCheckNumber = 4;
|
|
unsigned int frameCount = 0;
|
|
unsigned int g_TotalErrors = 0;
|
|
char *g_refFile = NULL;
|
|
|
|
const char *sSDKsample = "CUDA Particles Simulation";
|
|
|
|
extern "C" void cudaInit(int argc, char **argv);
|
|
extern "C" void cudaGLInit(int argc, char **argv);
|
|
extern "C" void copyArrayFromDevice(void *host, const void *device,
|
|
unsigned int vbo, int size);
|
|
|
|
// initialize particle system
|
|
void initParticleSystem(int numParticles, uint3 gridSize, bool bUseOpenGL) {
|
|
psystem = new ParticleSystem(numParticles, gridSize, bUseOpenGL);
|
|
psystem->reset(ParticleSystem::CONFIG_GRID);
|
|
|
|
if (bUseOpenGL) {
|
|
renderer = new ParticleRenderer;
|
|
renderer->setParticleRadius(psystem->getParticleRadius());
|
|
renderer->setColorBuffer(psystem->getColorBuffer());
|
|
}
|
|
|
|
sdkCreateTimer(&timer);
|
|
}
|
|
|
|
void cleanup() {
|
|
sdkDeleteTimer(&timer);
|
|
|
|
if (psystem) {
|
|
delete psystem;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// initialize OpenGL
|
|
void initGL(int *argc, char **argv) {
|
|
glutInit(argc, argv);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
|
|
glutInitWindowSize(width, height);
|
|
glutCreateWindow("CUDA Particles");
|
|
|
|
if (!isGLVersionSupported(2, 0) ||
|
|
!areGLExtensionsSupported(
|
|
"GL_ARB_multitexture GL_ARB_vertex_buffer_object")) {
|
|
fprintf(stderr, "Required OpenGL extensions missing.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
|
|
if (wglewIsSupported("WGL_EXT_swap_control")) {
|
|
// disable vertical sync
|
|
wglSwapIntervalEXT(0);
|
|
}
|
|
|
|
#endif
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClearColor(0.25, 0.25, 0.25, 1.0);
|
|
|
|
glutReportErrors();
|
|
}
|
|
|
|
void runBenchmark(int iterations, char *exec_path) {
|
|
printf("Run %u particles simulation for %d iterations...\n\n", numParticles,
|
|
iterations);
|
|
cudaDeviceSynchronize();
|
|
sdkStartTimer(&timer);
|
|
|
|
for (int i = 0; i < iterations; ++i) {
|
|
psystem->update(timestep);
|
|
}
|
|
|
|
cudaDeviceSynchronize();
|
|
sdkStopTimer(&timer);
|
|
float fAvgSeconds =
|
|
((float)1.0e-3 * (float)sdkGetTimerValue(&timer) / (float)iterations);
|
|
|
|
printf(
|
|
"particles, Throughput = %.4f KParticles/s, Time = %.5f s, Size = %u "
|
|
"particles, NumDevsUsed = %u, Workgroup = %u\n",
|
|
(1.0e-3 * numParticles) / fAvgSeconds, fAvgSeconds, numParticles, 1, 0);
|
|
|
|
if (g_refFile) {
|
|
printf("\nChecking result...\n\n");
|
|
float *hPos =
|
|
(float *)malloc(sizeof(float) * 4 * psystem->getNumParticles());
|
|
copyArrayFromDevice(hPos, psystem->getCudaPosVBO(), 0,
|
|
sizeof(float) * 4 * psystem->getNumParticles());
|
|
|
|
sdkDumpBin((void *)hPos, sizeof(float) * 4 * psystem->getNumParticles(),
|
|
"particles.bin");
|
|
|
|
if (!sdkCompareBin2BinFloat("particles.bin", g_refFile,
|
|
4 * psystem->getNumParticles(),
|
|
MAX_EPSILON_ERROR, THRESHOLD, exec_path)) {
|
|
g_TotalErrors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void computeFPS() {
|
|
frameCount++;
|
|
fpsCount++;
|
|
|
|
if (fpsCount == fpsLimit) {
|
|
char fps[256];
|
|
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
|
|
sprintf(fps, "CUDA Particles (%d particles): %3.1f fps", numParticles,
|
|
ifps);
|
|
|
|
glutSetWindowTitle(fps);
|
|
fpsCount = 0;
|
|
|
|
fpsLimit = (int)MAX(ifps, 1.f);
|
|
sdkResetTimer(&timer);
|
|
}
|
|
}
|
|
|
|
void display() {
|
|
sdkStartTimer(&timer);
|
|
|
|
// update the simulation
|
|
if (!bPause) {
|
|
psystem->setIterations(iterations);
|
|
psystem->setDamping(damping);
|
|
psystem->setGravity(-gravity);
|
|
psystem->setCollideSpring(collideSpring);
|
|
psystem->setCollideDamping(collideDamping);
|
|
psystem->setCollideShear(collideShear);
|
|
psystem->setCollideAttraction(collideAttraction);
|
|
|
|
psystem->update(timestep);
|
|
|
|
if (renderer) {
|
|
renderer->setVertexBuffer(psystem->getCurrentReadBuffer(),
|
|
psystem->getNumParticles());
|
|
}
|
|
}
|
|
|
|
// render
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// view transform
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
for (int c = 0; c < 3; ++c) {
|
|
camera_trans_lag[c] += (camera_trans[c] - camera_trans_lag[c]) * inertia;
|
|
camera_rot_lag[c] += (camera_rot[c] - camera_rot_lag[c]) * inertia;
|
|
}
|
|
|
|
glTranslatef(camera_trans_lag[0], camera_trans_lag[1], camera_trans_lag[2]);
|
|
glRotatef(camera_rot_lag[0], 1.0, 0.0, 0.0);
|
|
glRotatef(camera_rot_lag[1], 0.0, 1.0, 0.0);
|
|
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, modelView);
|
|
|
|
// cube
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
glutWireCube(2.0);
|
|
|
|
// collider
|
|
glPushMatrix();
|
|
float3 p = psystem->getColliderPos();
|
|
glTranslatef(p.x, p.y, p.z);
|
|
glColor3f(1.0, 0.0, 0.0);
|
|
glutSolidSphere(psystem->getColliderRadius(), 20, 10);
|
|
glPopMatrix();
|
|
|
|
if (renderer && displayEnabled) {
|
|
renderer->display(displayMode);
|
|
}
|
|
|
|
if (displaySliders) {
|
|
glDisable(GL_DEPTH_TEST);
|
|
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); // invert color
|
|
glEnable(GL_BLEND);
|
|
params->Render(0, 0);
|
|
glDisable(GL_BLEND);
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
sdkStopTimer(&timer);
|
|
|
|
glutSwapBuffers();
|
|
glutReportErrors();
|
|
|
|
computeFPS();
|
|
}
|
|
|
|
inline float frand() { return rand() / (float)RAND_MAX; }
|
|
|
|
void addSphere() {
|
|
// inject a sphere of particles
|
|
float pr = psystem->getParticleRadius();
|
|
float tr = pr + (pr * 2.0f) * ballr;
|
|
float pos[4], vel[4];
|
|
pos[0] = -1.0f + tr + frand() * (2.0f - tr * 2.0f);
|
|
pos[1] = 1.0f - tr;
|
|
pos[2] = -1.0f + tr + frand() * (2.0f - tr * 2.0f);
|
|
pos[3] = 0.0f;
|
|
vel[0] = vel[1] = vel[2] = vel[3] = 0.0f;
|
|
psystem->addSphere(0, pos, vel, ballr, pr * 2.0f);
|
|
}
|
|
|
|
void reshape(int w, int h) {
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(60.0, (float)w / (float)h, 0.1, 100.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glViewport(0, 0, w, h);
|
|
|
|
if (renderer) {
|
|
renderer->setWindowSize(w, h);
|
|
renderer->setFOV(60.0);
|
|
}
|
|
}
|
|
|
|
void mouse(int button, int state, int x, int y) {
|
|
int mods;
|
|
|
|
if (state == GLUT_DOWN) {
|
|
buttonState |= 1 << button;
|
|
} else if (state == GLUT_UP) {
|
|
buttonState = 0;
|
|
}
|
|
|
|
mods = glutGetModifiers();
|
|
|
|
if (mods & GLUT_ACTIVE_SHIFT) {
|
|
buttonState = 2;
|
|
} else if (mods & GLUT_ACTIVE_CTRL) {
|
|
buttonState = 3;
|
|
}
|
|
|
|
ox = x;
|
|
oy = y;
|
|
|
|
demoMode = false;
|
|
idleCounter = 0;
|
|
|
|
if (displaySliders) {
|
|
if (params->Mouse(x, y, button, state)) {
|
|
glutPostRedisplay();
|
|
return;
|
|
}
|
|
}
|
|
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
// transform vector by matrix
|
|
void xform(float *v, float *r, GLfloat *m) {
|
|
r[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + m[12];
|
|
r[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + m[13];
|
|
r[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + m[14];
|
|
}
|
|
|
|
// transform vector by transpose of matrix
|
|
void ixform(float *v, float *r, GLfloat *m) {
|
|
r[0] = v[0] * m[0] + v[1] * m[1] + v[2] * m[2];
|
|
r[1] = v[0] * m[4] + v[1] * m[5] + v[2] * m[6];
|
|
r[2] = v[0] * m[8] + v[1] * m[9] + v[2] * m[10];
|
|
}
|
|
|
|
void ixformPoint(float *v, float *r, GLfloat *m) {
|
|
float x[4];
|
|
x[0] = v[0] - m[12];
|
|
x[1] = v[1] - m[13];
|
|
x[2] = v[2] - m[14];
|
|
x[3] = 1.0f;
|
|
ixform(x, r, m);
|
|
}
|
|
|
|
void motion(int x, int y) {
|
|
float dx, dy;
|
|
dx = (float)(x - ox);
|
|
dy = (float)(y - oy);
|
|
|
|
if (displaySliders) {
|
|
if (params->Motion(x, y)) {
|
|
ox = x;
|
|
oy = y;
|
|
glutPostRedisplay();
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (mode) {
|
|
case M_VIEW:
|
|
if (buttonState == 3) {
|
|
// left+middle = zoom
|
|
camera_trans[2] += (dy / 100.0f) * 0.5f * fabs(camera_trans[2]);
|
|
} else if (buttonState & 2) {
|
|
// middle = translate
|
|
camera_trans[0] += dx / 100.0f;
|
|
camera_trans[1] -= dy / 100.0f;
|
|
} else if (buttonState & 1) {
|
|
// left = rotate
|
|
camera_rot[0] += dy / 5.0f;
|
|
camera_rot[1] += dx / 5.0f;
|
|
}
|
|
|
|
break;
|
|
|
|
case M_MOVE: {
|
|
float translateSpeed = 0.003f;
|
|
float3 p = psystem->getColliderPos();
|
|
|
|
if (buttonState == 1) {
|
|
float v[3], r[3];
|
|
v[0] = dx * translateSpeed;
|
|
v[1] = -dy * translateSpeed;
|
|
v[2] = 0.0f;
|
|
ixform(v, r, modelView);
|
|
p.x += r[0];
|
|
p.y += r[1];
|
|
p.z += r[2];
|
|
} else if (buttonState == 2) {
|
|
float v[3], r[3];
|
|
v[0] = 0.0f;
|
|
v[1] = 0.0f;
|
|
v[2] = dy * translateSpeed;
|
|
ixform(v, r, modelView);
|
|
p.x += r[0];
|
|
p.y += r[1];
|
|
p.z += r[2];
|
|
}
|
|
|
|
psystem->setColliderPos(p);
|
|
} break;
|
|
}
|
|
|
|
ox = x;
|
|
oy = y;
|
|
|
|
demoMode = false;
|
|
idleCounter = 0;
|
|
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
// commented out to remove unused parameter warnings in Linux
|
|
void key(unsigned char key, int /*x*/, int /*y*/) {
|
|
switch (key) {
|
|
case ' ':
|
|
bPause = !bPause;
|
|
break;
|
|
|
|
case 13:
|
|
psystem->update(timestep);
|
|
|
|
if (renderer) {
|
|
renderer->setVertexBuffer(psystem->getCurrentReadBuffer(),
|
|
psystem->getNumParticles());
|
|
}
|
|
|
|
break;
|
|
|
|
case '\033':
|
|
case 'q':
|
|
#if defined(__APPLE__) || defined(MACOSX)
|
|
exit(EXIT_SUCCESS);
|
|
#else
|
|
glutDestroyWindow(glutGetWindow());
|
|
return;
|
|
#endif
|
|
case 'v':
|
|
mode = M_VIEW;
|
|
break;
|
|
|
|
case 'm':
|
|
mode = M_MOVE;
|
|
break;
|
|
|
|
case 'p':
|
|
displayMode = (ParticleRenderer::DisplayMode)(
|
|
(displayMode + 1) % ParticleRenderer::PARTICLE_NUM_MODES);
|
|
break;
|
|
|
|
case 'd':
|
|
psystem->dumpGrid();
|
|
break;
|
|
|
|
case 'u':
|
|
psystem->dumpParticles(0, numParticles - 1);
|
|
break;
|
|
|
|
case 'r':
|
|
displayEnabled = !displayEnabled;
|
|
break;
|
|
|
|
case '1':
|
|
psystem->reset(ParticleSystem::CONFIG_GRID);
|
|
break;
|
|
|
|
case '2':
|
|
psystem->reset(ParticleSystem::CONFIG_RANDOM);
|
|
break;
|
|
|
|
case '3':
|
|
addSphere();
|
|
break;
|
|
|
|
case '4': {
|
|
// shoot ball from camera
|
|
float pr = psystem->getParticleRadius();
|
|
float vel[4], velw[4], pos[4], posw[4];
|
|
vel[0] = 0.0f;
|
|
vel[1] = 0.0f;
|
|
vel[2] = -0.05f;
|
|
vel[3] = 0.0f;
|
|
ixform(vel, velw, modelView);
|
|
|
|
pos[0] = 0.0f;
|
|
pos[1] = 0.0f;
|
|
pos[2] = -2.5f;
|
|
pos[3] = 1.0;
|
|
ixformPoint(pos, posw, modelView);
|
|
posw[3] = 0.0f;
|
|
|
|
psystem->addSphere(0, posw, velw, ballr, pr * 2.0f);
|
|
} break;
|
|
|
|
case 'w':
|
|
wireframe = !wireframe;
|
|
break;
|
|
|
|
case 'h':
|
|
displaySliders = !displaySliders;
|
|
break;
|
|
}
|
|
|
|
demoMode = false;
|
|
idleCounter = 0;
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void special(int k, int x, int y) {
|
|
if (displaySliders) {
|
|
params->Special(k, x, y);
|
|
}
|
|
|
|
demoMode = false;
|
|
idleCounter = 0;
|
|
}
|
|
|
|
void idle(void) {
|
|
if ((idleCounter++ > idleDelay) && (demoMode == false)) {
|
|
demoMode = true;
|
|
printf("Entering demo mode\n");
|
|
}
|
|
|
|
if (demoMode) {
|
|
camera_rot[1] += 0.1f;
|
|
|
|
if (demoCounter++ > 1000) {
|
|
ballr = 10 + (rand() % 10);
|
|
addSphere();
|
|
demoCounter = 0;
|
|
}
|
|
}
|
|
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void initParams() {
|
|
if (g_refFile) {
|
|
timestep = 0.0f;
|
|
damping = 0.0f;
|
|
gravity = 0.0f;
|
|
ballr = 1;
|
|
collideSpring = 0.0f;
|
|
collideDamping = 0.0f;
|
|
collideShear = 0.0f;
|
|
collideAttraction = 0.0f;
|
|
} else {
|
|
// create a new parameter list
|
|
params = new ParamListGL("misc");
|
|
params->AddParam(
|
|
new Param<float>("time step", timestep, 0.0f, 1.0f, 0.01f, ×tep));
|
|
params->AddParam(
|
|
new Param<float>("damping", damping, 0.0f, 1.0f, 0.001f, &damping));
|
|
params->AddParam(
|
|
new Param<float>("gravity", gravity, 0.0f, 0.001f, 0.0001f, &gravity));
|
|
params->AddParam(new Param<int>("ball radius", ballr, 1, 20, 1, &ballr));
|
|
|
|
params->AddParam(new Param<float>("collide spring", collideSpring, 0.0f,
|
|
1.0f, 0.001f, &collideSpring));
|
|
params->AddParam(new Param<float>("collide damping", collideDamping, 0.0f,
|
|
0.1f, 0.001f, &collideDamping));
|
|
params->AddParam(new Param<float>("collide shear", collideShear, 0.0f, 0.1f,
|
|
0.001f, &collideShear));
|
|
params->AddParam(new Param<float>("collide attract", collideAttraction,
|
|
0.0f, 0.1f, 0.001f, &collideAttraction));
|
|
}
|
|
}
|
|
|
|
void mainMenu(int i) { key((unsigned char)i, 0, 0); }
|
|
|
|
void initMenus() {
|
|
glutCreateMenu(mainMenu);
|
|
glutAddMenuEntry("Reset block [1]", '1');
|
|
glutAddMenuEntry("Reset random [2]", '2');
|
|
glutAddMenuEntry("Add sphere [3]", '3');
|
|
glutAddMenuEntry("View mode [v]", 'v');
|
|
glutAddMenuEntry("Move cursor mode [m]", 'm');
|
|
glutAddMenuEntry("Toggle point rendering [p]", 'p');
|
|
glutAddMenuEntry("Toggle animation [ ]", ' ');
|
|
glutAddMenuEntry("Step animation [ret]", 13);
|
|
glutAddMenuEntry("Toggle sliders [h]", 'h');
|
|
glutAddMenuEntry("Quit (esc)", '\033');
|
|
glutAttachMenu(GLUT_RIGHT_BUTTON);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Program main
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int main(int argc, char **argv) {
|
|
#if defined(__linux__)
|
|
setenv("DISPLAY", ":0", 0);
|
|
#endif
|
|
|
|
printf("%s Starting...\n\n", sSDKsample);
|
|
|
|
printf(
|
|
"NOTE: The CUDA Samples are not meant for performance measurements. "
|
|
"Results may vary when GPU Boost is enabled.\n\n");
|
|
|
|
numParticles = NUM_PARTICLES;
|
|
uint gridDim = GRID_SIZE;
|
|
numIterations = 0;
|
|
|
|
if (argc > 1) {
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "n")) {
|
|
numParticles = getCmdLineArgumentInt(argc, (const char **)argv, "n");
|
|
}
|
|
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "grid")) {
|
|
gridDim = getCmdLineArgumentInt(argc, (const char **)argv, "grid");
|
|
}
|
|
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
|
|
getCmdLineArgumentString(argc, (const char **)argv, "file", &g_refFile);
|
|
fpsLimit = frameCheckNumber;
|
|
numIterations = 1;
|
|
}
|
|
}
|
|
|
|
gridSize.x = gridSize.y = gridSize.z = gridDim;
|
|
printf("grid: %d x %d x %d = %d cells\n", gridSize.x, gridSize.y, gridSize.z,
|
|
gridSize.x * gridSize.y * gridSize.z);
|
|
printf("particles: %d\n", numParticles);
|
|
|
|
bool benchmark =
|
|
checkCmdLineFlag(argc, (const char **)argv, "benchmark") != 0;
|
|
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "i")) {
|
|
numIterations = getCmdLineArgumentInt(argc, (const char **)argv, "i");
|
|
}
|
|
|
|
if (benchmark || g_refFile) {
|
|
cudaInit(argc, argv);
|
|
} else {
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "device")) {
|
|
printf("[%s]\n", argv[0]);
|
|
printf(" Does not explicitly support -device=n in OpenGL mode\n");
|
|
printf(" To use -device=n, the sample must be running w/o OpenGL\n\n");
|
|
printf(" > %s -device=n -file=<*.bin>\n", argv[0]);
|
|
printf("exiting...\n");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
initGL(&argc, argv);
|
|
cudaInit(argc, argv);
|
|
}
|
|
|
|
initParticleSystem(numParticles, gridSize, !benchmark && g_refFile == NULL);
|
|
initParams();
|
|
|
|
if (benchmark || g_refFile) {
|
|
if (numIterations <= 0) {
|
|
numIterations = 300;
|
|
}
|
|
|
|
runBenchmark(numIterations, argv[0]);
|
|
} else {
|
|
if (!g_refFile) {
|
|
initMenus();
|
|
}
|
|
|
|
glutDisplayFunc(display);
|
|
glutReshapeFunc(reshape);
|
|
glutMouseFunc(mouse);
|
|
glutMotionFunc(motion);
|
|
glutKeyboardFunc(key);
|
|
glutSpecialFunc(special);
|
|
glutIdleFunc(idle);
|
|
|
|
glutCloseFunc(cleanup);
|
|
|
|
glutMainLoop();
|
|
}
|
|
|
|
if (psystem) {
|
|
delete psystem;
|
|
}
|
|
|
|
exit(g_TotalErrors > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|