www.pudn.com > helloGPGPU-1.0.2.zip > helloGPGPU.cpp
//--------------------------------------------------------------------------- // www.GPGPU.org // Sample Code //--------------------------------------------------------------------------- // Copyright (c) 2004 Mark J. Harris and GPGPU.org //--------------------------------------------------------------------------- // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any // damages arising from the use of this software. // // Permission is granted to anyone to use this software for any // purpose, including commercial applications, and to alter it and // redistribute it freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you // must not claim that you wrote the original software. If you use // this software in a product, an acknowledgment in the product // documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and // must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //--------------------------------------------------------------------------- // Author: Mark Harris (harrism@gpgpu.org) //--------------------------------------------------------------------------- // GPGPU Lesson 0: "helloGPGPU". //--------------------------------------------------------------------------- // // GPGPU CONCEPTS Introduced: // // 1.) Texture = Array // 2.) Fragment Program = Computational Kernel. // 3.) One-to-one Pixel to Texel Mapping: // a) Data-Dimensioned Viewport, and // b) Orthographic Projection. // 4.) Viewport-Sized Quad = Data Stream Generator. // 5.) Copy To Texture = feedback. // // For details of each of these concepts, see the explanations in the // inline "GPGPU CONCEPT" comments in the code below. // // APPLICATION Demonstrated: A simple post-process edge detection filter. // //--------------------------------------------------------------------------- #include#include #include #define GLEW_STATIC 1 #include #include // forward declarations class HelloGPGPU; void reshape(int w, int h); // globals CGcontext g_cgContext; CGprofile g_cgProfile; HelloGPGPU *g_pHello; // This shader performs a 9-tap Laplacian edge detection filter. static const char *edgeFragSource = "half4 edges(half2 coords : TEX0, \n" " uniform sampler2D texture) : COLOR \n" "{ \n" " static const half offset = 1.0 / 512.0; \n" " half4 c = tex2D(texture, coords); \n" " half4 bl = tex2D(texture, coords + half2(-offset, -offset)); \n" " half4 l = tex2D(texture, coords + half2(-offset, 0)); \n" " half4 tl = tex2D(texture, coords + half2(-offset, offset)); \n" " half4 t = tex2D(texture, coords + half2( 0, offset)); \n" " half4 ur = tex2D(texture, coords + half2( offset, offset)); \n" " half4 r = tex2D(texture, coords + half2( offset, 0)); \n" " half4 br = tex2D(texture, coords + half2( offset, offset)); \n" " half4 b = tex2D(texture, coords + half2( 0, -offset)); \n" " // scale by 8 to brighten the edges \n" " return 8 * (c + -0.125 * (bl + l + tl + t + ur + r + br + b)); \n" "} \n"; // This class encapsulates all of the GPGPU functionality of the example. class HelloGPGPU { public: // methods HelloGPGPU(int w, int h) : _rAngle(0), _iWidth(w), _iHeight(h) { // Create a simple 2D texture. This example does not use // render to texture -- it just copies from the framebuffer to the // texture. // GPGPU CONCEPT 1: Texture = Array. // Textures are the GPGPU equivalent of arrays in standard // computation. Here we allocate a texture large enough to fit our // data (which is arbitrary in this example). glGenTextures(1, &_iTexture); glBindTexture(GL_TEXTURE_2D, _iTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _iWidth, _iHeight, 0, GL_RGB, GL_FLOAT, 0); // GPGPU CONCEPT 2: Fragment Program = Computational Kernel. // A fragment program can be thought of as a small computational // kernel that is applied in parallel to many fragments // simultaneously. Here we load a kernel that performs an edge // detection filter on an image. // Create the edge detection fragment program _fragmentProgram = cgCreateProgram(g_cgContext, CG_SOURCE, edgeFragSource, g_cgProfile, "edges", NULL); // Create the texture parameter for the fragment program if(_fragmentProgram != NULL) { cgGLLoadProgram(_fragmentProgram); _textureParam = cgGetNamedParameter(_fragmentProgram, "texture"); } } ~HelloGPGPU() { cgDestroyProgram(_fragmentProgram); } // This method updates the texture by rendering the geometry (a teapot // and 3 rotating tori) and copying the image to a texture. // It then renders a second pass using the texture as input to an edge // detection filter. It copies the results of the filter to the texture. // The texture is used in HelloGPGPU::display() for displaying the // results. void update() { _rAngle += 0.5f; // store the window viewport dimensions so we can reset them, // and set the viewport to the dimensions of our texture int vp[4]; glGetIntegerv(GL_VIEWPORT, vp); // GPGPU CONCEPT 3a: One-to-one Pixel to Texel Mapping: A Data- // Dimensioned Viewport. // We need a one-to-one mapping of pixels to texels in order to // ensure every element of our texture is processed. By setting our // viewport to the dimensions of our destination texture and drawing // a screen-sized quad (see below), we ensure that every pixel of our // texel is generated and processed in the fragment program. glViewport(0, 0, _iWidth, _iHeight); // Render a teapot and 3 tori glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glRotatef(-_rAngle, 0, 1, 0.25); glutSolidTeapot(0.5); glPopMatrix(); glPushMatrix(); glRotatef(2.1 * _rAngle, 1, 0.5, 0); glutSolidTorus(0.05, 0.9, 64, 64); glPopMatrix(); glPushMatrix(); glRotatef(-1.5 * _rAngle, 0, 1, 0.5); glutSolidTorus(0.05, 0.9, 64, 64); glPopMatrix(); glPushMatrix(); glRotatef(1.78 * _rAngle, 0.5, 0, 1); glutSolidTorus(0.05, 0.9, 64, 64); glPopMatrix(); // copy the results to the texture glBindTexture(GL_TEXTURE_2D, _iTexture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _iWidth, _iHeight); // run the edge detection filter over the geometry texture // Activate the edge detection filter program cgGLBindProgram(_fragmentProgram); cgGLEnableProfile(g_cgProfile); // bind the scene texture as input to the filter cgGLSetTextureParameter(_textureParam, _iTexture); cgGLEnableTextureParameter(_textureParam); // GPGPU CONCEPT 4: Viewport-Sized Quad = Data Stream Generator. // In order to execute fragment programs, we need to generate pixels. // Drawing a quad the size of our viewport (see above) generates a // fragment for every pixel of our destination texture. Each fragment // is processed identically by the fragment program. Notice that in // the reshape() function, below, we have set the frustum to // orthographic, and the frustum dimensions to [-1,1]. Thus, our // viewport-sized quad vertices are at [-1,-1], [1,-1], [1,1], and // [-1,1]: the corners of the viewport. glBegin(GL_QUADS); { glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.5f); glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.5f); glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.5f); glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.5f); } glEnd(); // disable the filter cgGLDisableTextureParameter(_textureParam); cgGLDisableProfile(g_cgProfile); // GPGPU CONCEPT 5: Copy To Texture (CTT) = Feedback. // We have just invoked our computation (edge detection) by applying // a fragment program to a viewport-sized quad. The results are now // in the frame buffer. To store them, we copy the data from the // frame buffer to a texture. This can then be fed back as input // for display (in this case) or more computation (see // more advanced samples.) // update the texture again, this time with the filtered scene glBindTexture(GL_TEXTURE_2D, _iTexture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _iWidth, _iHeight); // restore the stored viewport dimensions glViewport(vp[0], vp[1], vp[2], vp[3]); } void display() { // Bind the filtered texture glBindTexture(GL_TEXTURE_2D, _iTexture); glEnable(GL_TEXTURE_2D); // render a full-screen quad textured with the results of our // computation. Note that this is not part of the computation: this // is only the visualization of the results. glBegin(GL_QUADS); { glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.5f); glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.5f); glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.5f); glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.5f); } glEnd(); glDisable(GL_TEXTURE_2D); } protected: // data int _iWidth, _iHeight; // The dimensions of our array float _rAngle; // used for animation unsigned int _iTexture; // The texture used as a data array CGprogram _fragmentProgram; // the fragment program used to update CGparameter _textureParam; // a parameter to the fragment program }; // GLUT idle function void idle() { glutPostRedisplay(); } // GLUT display function void display() { g_pHello->update(); // update the scene and run the edge detect filter g_pHello->display(); // display the results glutSwapBuffers(); } // GLUT reshape function void reshape(int w, int h) { if (h == 0) h = 1; glViewport(0, 0, w, h); // GPGPU CONCEPT 3b: One-to-one Pixel to Texel Mapping: An Orthographic // Projection. // This code sets the projection matrix to orthographic with a range of // [-1,1] in the X and Y dimensions. This allows a trivial mapping of // pixels to texels. glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-1, 1, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // Called when Cg detects an error void cgErrorCallback() { CGerror lastError = cgGetError(); if(lastError) { printf("%s\n\n", cgGetErrorString(lastError)); printf("%s\n", cgGetLastListing(g_cgContext)); printf("Cg error!\n"); } } // Called at startup void initialize() { // Setup Cg cgSetErrorCallback(cgErrorCallback); g_cgContext = cgCreateContext(); // get the best profile for this hardware g_cgProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT); assert(g_cgProfile != CG_PROFILE_UNKNOWN); cgGLSetOptimalOptions(g_cgProfile); // Create the example object g_pHello = new HelloGPGPU(512, 512); } // The main function void main() { glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowSize(512, 512); glutCreateWindow("Hello, GPGPU!"); glutIdleFunc(idle); glutDisplayFunc(display); glutReshapeFunc(reshape); initialize(); glutMainLoop(); }