/* * Measure glTexSubImage and glCopyTexSubImage speed * * Brian Paul * 26 Jan 2006 */ #define GL_GLEXT_PROTOTYPES #include #include #include #include #include static GLint WinWidth = 1024, WinHeight = 512; static GLint TexWidth = 512, TexHeight = 512; static GLuint TexObj = 1; static GLenum IntFormat = GL_RGBA8; static GLenum ReadFormat = GL_RGBA; /* for glReadPixels */ static GLboolean DrawQuad = GL_TRUE; /** * draw teapot image, size TexWidth by TexHeight */ static void DrawTestImage(void) { GLfloat ar; glViewport(0, 0, TexWidth, TexHeight); glScissor(0, 0, TexWidth, TexHeight); glEnable(GL_SCISSOR_TEST); glClearColor(0.5, 0.5, 0.5, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ar = (float) TexWidth / TexHeight; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-ar, ar, -1.0, 1.0, 5.0, 25.0); glMatrixMode(GL_MODELVIEW); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glFrontFace(GL_CW); glPushMatrix(); glRotatef(45, 1, 0, 0); glRotatef(45, 0, 1, 0); glutSolidTeapot(2.3); glPopMatrix(); glFrontFace(GL_CCW); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_SCISSOR_TEST); glViewport(0, 0, WinWidth, WinHeight); glFinish(); } /** * Do glCopyTexSubImage2D call (update texture with framebuffer data) * If doSubRect is true, do the copy in four pieces instead of all at once. */ static void DoCopyTex(GLboolean doSubRect) { if (doSubRect) { /* copy in four parts */ int w = TexWidth / 2, h = TexHeight / 2; int x0 = 0, y0 = 0; int x1 = w, y1 = h; #if 1 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, x0, y0, w, h); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x1, y0, x1, y0, w, h); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x0, y1, x0, y1, w, h); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x1, y1, x1, y1, w, h); #else /* scramble */ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, x1, y1, w, h); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x1, y0, x0, y1, w, h); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x0, y1, x1, y0, w, h); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x1, y1, x0, y0, w, h); #endif } else { glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, TexWidth, TexHeight); } } /** * Do glTexSubImage2D (update texture w/ user data) * If doSubRect, do update in four pieces, else all at once. */ static void SubTex(GLboolean doSubRect, const GLubyte *image) { if (doSubRect) { /* four pieces */ int w = TexWidth / 2, h = TexHeight / 2; int x0 = 0, y0 = 0; int x1 = w, y1 = h; glPixelStorei(GL_UNPACK_ROW_LENGTH, TexWidth); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_SKIP_ROWS, y0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, x0); glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, w, h, ReadFormat, GL_UNSIGNED_BYTE, image); glPixelStorei(GL_UNPACK_SKIP_ROWS, y0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, x1); glTexSubImage2D(GL_TEXTURE_2D, 0, x1, y0, w, h, ReadFormat, GL_UNSIGNED_BYTE, image); glPixelStorei(GL_UNPACK_SKIP_ROWS, y1); glPixelStorei(GL_UNPACK_SKIP_PIXELS, x0); glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y1, w, h, ReadFormat, GL_UNSIGNED_BYTE, image); glPixelStorei(GL_UNPACK_SKIP_ROWS, y1); glPixelStorei(GL_UNPACK_SKIP_PIXELS, x1); glTexSubImage2D(GL_TEXTURE_2D, 0, x1, y1, w, h, ReadFormat, GL_UNSIGNED_BYTE, image); } else { /* all at once */ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TexWidth, TexHeight, ReadFormat, GL_UNSIGNED_BYTE, image); } } /** * Measure gl[Copy]TexSubImage rate. * This actually also includes time to render a quad and SwapBuffers. */ static void RunTest(GLboolean copyTex, GLboolean doSubRect) { double t0, t1; int iters = 0; float copyRate, mbRate; float rot = 0.0; int bpp, r, g, b, a; int w, h; GLubyte *image = NULL; glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_RED_SIZE, &r); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_GREEN_SIZE, &g); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BLUE_SIZE, &b); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &a); bpp = (r + g + b + a) / 8; if (!copyTex) { /* read image from frame buffer */ image = (GLubyte *) malloc(TexWidth * TexHeight * bpp); glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, TexWidth, TexHeight, ReadFormat, GL_UNSIGNED_BYTE, image); } glEnable(GL_TEXTURE_2D); glViewport(WinWidth / 2, 0, WinWidth / 2, WinHeight); t0 = glutGet(GLUT_ELAPSED_TIME) / 1000.0; do { if (copyTex) /* Framebuffer -> Texture */ DoCopyTex(doSubRect); else { /* Main Mem -> Texture */ SubTex(doSubRect, image); } /* draw textured quad */ if (DrawQuad) { glPushMatrix(); glRotatef(rot, 0, 0, 1); glTranslatef(1, 0, 0); glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex2f(-1, -1); glTexCoord2f(1, 0); glVertex2f( 1, -1); glTexCoord2f(1, 1); glVertex2f( 1, 1); glTexCoord2f(0, 1); glVertex2f(-1, 1); glEnd(); glPopMatrix(); } iters++; rot += 2.0; t1 = glutGet(GLUT_ELAPSED_TIME) / 1000.0; if (DrawQuad) { glutSwapBuffers(); } } while (t1 - t0 < 5.0); glDisable(GL_TEXTURE_2D); if (image) free(image); if (doSubRect) { w = TexWidth / 2; h = TexHeight / 2; iters *= 4; } else { w = TexWidth; h = TexHeight; } copyRate = iters / (t1 - t0); mbRate = w * h * bpp * copyRate / (1024 * 1024); if (copyTex) printf("glCopyTexSubImage: %d x %d, %d Bpp:\n", w, h, bpp); else printf("glTexSubImage: %d x %d, %d Bpp:\n", w, h, bpp); printf(" %d calls in %.2f = %.2f calls/sec, %.2f MB/s\n", iters, t1-t0, copyRate, mbRate); } static void Draw(void) { glClearColor(0.2, 0.2, 0.8, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); DrawTestImage(); if (!DrawQuad) { glutSwapBuffers(); } RunTest(GL_FALSE, GL_FALSE); RunTest(GL_FALSE, GL_TRUE); RunTest(GL_TRUE, GL_FALSE); RunTest(GL_TRUE, GL_TRUE); glutSwapBuffers(); printf("exiting\n"); exit(0); } static void Reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -15.0); } static void Key(unsigned char key, int x, int y) { (void) x; (void) y; switch (key) { case 27: exit(0); break; } glutPostRedisplay(); } static void SpecialKey(int key, int x, int y) { (void) x; (void) y; switch (key) { case GLUT_KEY_UP: break; case GLUT_KEY_DOWN: break; case GLUT_KEY_LEFT: break; case GLUT_KEY_RIGHT: break; } glutPostRedisplay(); } static void Init(void) { /* create initial, empty teximage */ glBindTexture(GL_TEXTURE_2D, TexObj); glTexImage2D(GL_TEXTURE_2D, 0, IntFormat, TexWidth, TexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } static void ParseArgs(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-nodraw") == 0) DrawQuad = GL_FALSE; } } int main(int argc, char *argv[]) { GLint mode = GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE | GLUT_DEPTH; glutInit(&argc, argv); ParseArgs(argc, argv); glutInitWindowPosition(0, 0); glutInitWindowSize(WinWidth, WinHeight); glutInitDisplayMode(mode); glutCreateWindow(argv[0]); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutSpecialFunc(SpecialKey); glutDisplayFunc(Draw); printf("GL_RENDERER: %s\n", (char *) glGetString(GL_RENDERER)); Init(); glutMainLoop(); return 0; }