1 2/* 3 * Copyright (C) 1999 Brian Paul All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included 13 * in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 23 24/* 25 * texdown 26 * 27 * Measure texture download speed. 28 * Use keyboard to change texture size, format, datatype, scale/bias, 29 * subimageload, etc. 30 * 31 * Brian Paul 28 January 2000 32 */ 33 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <malloc.h> 38#include <math.h> 39#include <GL/glew.h> 40#include "glut_wrap.h" 41 42 43static GLsizei MaxSize = 2048; 44static GLsizei TexWidth = 1024, TexHeight = 1024, TexBorder = 0; 45static GLboolean ScaleAndBias = GL_FALSE; 46static GLboolean SubImage = GL_FALSE; 47static GLdouble DownloadRate = 0.0; /* texels/sec */ 48 49static GLuint Mode = 0; 50 51 52/* Try and avoid L2 cache effects by cycling through a small number of 53 * textures. 54 * 55 * At the initial size of 1024x1024x4 == 4mbyte, say 8 textures will 56 * keep us out of most caches at 32mb total. 57 * 58 * This turns into a fairly interesting question of what exactly you 59 * expect to be in cache in normal usage, and what you think should be 60 * outside. There's no rules for this, no reason to favour one usage 61 * over another except what the application you care about happens to 62 * resemble most closely. 63 * 64 * - Should the client texture image be in L2 cache? Has it just been 65 * generated or read from disk? 66 * - Does the application really use >1 texture, or is it constantly 67 * updating one image in-place? 68 * 69 * Different answers will favour different texture upload mechanisms. 70 * To upload an image that is purely outside of cache, a DMA-based 71 * upload will probably win, whereas for small, in-cache textures, 72 * copying looks good. 73 */ 74#define NR_TEXOBJ 4 75static GLuint TexObj[NR_TEXOBJ]; 76 77 78struct FormatRec { 79 GLenum Format; 80 GLenum Type; 81 GLenum IntFormat; 82 GLint TexelSize; 83}; 84 85 86static const struct FormatRec FormatTable[] = { 87 /* Format Type IntFormat TexelSize */ 88 { GL_BGRA, GL_UNSIGNED_BYTE, GL_RGBA, 4 }, 89 { GL_RGB, GL_UNSIGNED_BYTE, GL_RGB, 3 }, 90 { GL_RGBA, GL_UNSIGNED_BYTE, GL_RGBA, 4 }, 91 { GL_RGBA, GL_UNSIGNED_BYTE, GL_RGB, 4 }, 92 { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_RGB, 2 }, 93 { GL_LUMINANCE, GL_UNSIGNED_BYTE, GL_LUMINANCE, 1 }, 94 { GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GL_LUMINANCE_ALPHA, 2 }, 95 { GL_ALPHA, GL_UNSIGNED_BYTE, GL_ALPHA, 1 }, 96}; 97static GLint Format; 98 99#define NUM_FORMATS (sizeof(FormatTable)/sizeof(FormatTable[0])) 100 101static int 102BytesPerTexel(GLint format) 103{ 104 return FormatTable[format].TexelSize; 105} 106 107 108static const char * 109FormatStr(GLenum format) 110{ 111 switch (format) { 112 case GL_RGB: 113 return "GL_RGB"; 114 case GL_RGBA: 115 return "GL_RGBA"; 116 case GL_BGRA: 117 return "GL_BGRA"; 118 case GL_LUMINANCE: 119 return "GL_LUMINANCE"; 120 case GL_LUMINANCE_ALPHA: 121 return "GL_LUMINANCE_ALPHA"; 122 case GL_ALPHA: 123 return "GL_ALPHA"; 124 default: 125 return ""; 126 } 127} 128 129 130static const char * 131TypeStr(GLenum type) 132{ 133 switch (type) { 134 case GL_UNSIGNED_BYTE: 135 return "GL_UNSIGNED_BYTE"; 136 case GL_UNSIGNED_SHORT: 137 return "GL_UNSIGNED_SHORT"; 138 case GL_UNSIGNED_SHORT_5_6_5: 139 return "GL_UNSIGNED_SHORT_5_6_5"; 140 case GL_UNSIGNED_SHORT_5_6_5_REV: 141 return "GL_UNSIGNED_SHORT_5_6_5_REV"; 142 default: 143 return ""; 144 } 145} 146 147/* On x86, there is a performance cliff for memcpy to texture memory 148 * for sources below 64 byte alignment. We do our best with this in 149 * the driver, but it is better if the images are correctly aligned to 150 * start with: 151 */ 152#define ALIGN (1<<12) 153 154static unsigned long align(unsigned long value, unsigned long a) 155{ 156 return (value + a - 1) & ~(a-1); 157} 158 159static void 160MeasureDownloadRate(void) 161{ 162 const int w = TexWidth + 2 * TexBorder; 163 const int h = TexHeight + 2 * TexBorder; 164 const int image_bytes = align(w * h * BytesPerTexel(Format), ALIGN); 165 const int bytes = image_bytes * NR_TEXOBJ; 166 GLubyte *texImage; 167 GLdouble t0, t1, time; 168 int count; 169 int i; 170 int offset = 0; 171 GLdouble total = 0; /* ints will tend to overflow */ 172 173 printf("allocating %d bytes for %d %dx%d images\n", 174 bytes, NR_TEXOBJ, w, h); 175 176#ifdef _WIN32 177 texImage = (GLubyte *) _aligned_malloc(bytes, ALIGN); 178#else 179 texImage = (GLubyte *) aligned_alloc(ALIGN, bytes); 180#endif 181 if (!texImage) { 182 DownloadRate = 0.0; 183 return; 184 } 185 186 printf("alloc %p\n", texImage); 187 188 for (i = 1; !(((unsigned long)texImage) & i); i<<=1) 189 ; 190 printf("texture image alignment: %d bytes (%p)\n", i, texImage); 191 192 for (i = 0; i < bytes; i++) { 193 texImage[i] = i & 0xff; 194 } 195 196 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 197 glPixelStorei(GL_PACK_ALIGNMENT, 1); 198 199 if (ScaleAndBias) { 200 glPixelTransferf(GL_RED_SCALE, 0.5); 201 glPixelTransferf(GL_GREEN_SCALE, 0.5); 202 glPixelTransferf(GL_BLUE_SCALE, 0.5); 203 glPixelTransferf(GL_RED_BIAS, 0.5); 204 glPixelTransferf(GL_GREEN_BIAS, 0.5); 205 glPixelTransferf(GL_BLUE_BIAS, 0.5); 206 } 207 else { 208 glPixelTransferf(GL_RED_SCALE, 1.0); 209 glPixelTransferf(GL_GREEN_SCALE, 1.0); 210 glPixelTransferf(GL_BLUE_SCALE, 1.0); 211 glPixelTransferf(GL_RED_BIAS, 0.0); 212 glPixelTransferf(GL_GREEN_BIAS, 0.0); 213 glPixelTransferf(GL_BLUE_BIAS, 0.0); 214 } 215 216 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 217 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 218 glEnable(GL_TEXTURE_2D); 219 220 count = 0; 221 t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001; 222 do { 223 int img = count%NR_TEXOBJ; 224 GLubyte *img_ptr = texImage + img * image_bytes; 225 226 glBindTexture(GL_TEXTURE_2D, TexObj[img]); 227 228 if (SubImage && count > 0) { 229 /* Only update a portion of the image each iteration. This 230 * is presumably why you'd want to use texsubimage, otherwise 231 * you may as well just call teximage again. 232 * 233 * A bigger question is whether to use a pointer that moves 234 * with each call, ie does the incoming data come from L2 235 * cache under normal circumstances, or is it pulled from 236 * uncached memory? 237 * 238 * There's a good argument to say L2 cache, ie you'd expect 239 * the data to have been recently generated. It's possible 240 * that it could have come from a file read, which may or may 241 * not have gone through the cpu. 242 */ 243 glTexSubImage2D(GL_TEXTURE_2D, 0, 244 -TexBorder, 245 -TexBorder + offset * h/8, 246 w, 247 h/8, 248 FormatTable[Format].Format, 249 FormatTable[Format].Type, 250#if 1 251 texImage /* likely in L2$ */ 252#else 253 img_ptr + offset * bytes/8 /* unlikely in L2$ */ 254#endif 255 ); 256 offset += 1; 257 offset %= 8; 258 total += w * h / 8; 259 } 260 else { 261 glTexImage2D(GL_TEXTURE_2D, 0, 262 FormatTable[Format].IntFormat, w, h, TexBorder, 263 FormatTable[Format].Format, 264 FormatTable[Format].Type, 265 img_ptr); 266 total += w*h; 267 } 268 269 /* draw a tiny polygon to force texture into texram */ 270 glBegin(GL_TRIANGLES); 271 glTexCoord2f(0, 0); glVertex2f(1, 1); 272 glTexCoord2f(1, 0); glVertex2f(3, 1); 273 glTexCoord2f(0.5, 1); glVertex2f(2, 3); 274 glEnd(); 275 276 t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001; 277 time = t1 - t0; 278 count++; 279 } while (time < 3.0); 280 281 glDisable(GL_TEXTURE_2D); 282 283 printf("total texels=%f time=%f\n", total, time); 284 DownloadRate = total / time; 285 286 287#ifdef _WIN32 288 _aligned_free(texImage); 289#else 290 free(texImage); 291#endif 292 293 { 294 GLint err = glGetError(); 295 if (err) 296 printf("GL error %d\n", err); 297 } 298} 299 300 301static void 302PrintString(const char *s) 303{ 304 while (*s) { 305 glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s); 306 s++; 307 } 308} 309 310 311static void 312Display(void) 313{ 314 const int w = TexWidth + 2 * TexBorder; 315 const int h = TexHeight + 2 * TexBorder; 316 char s[1000]; 317 318 glClear(GL_COLOR_BUFFER_BIT); 319 320 glRasterPos2i(10, 80); 321 sprintf(s, "Texture size[cursor]: %d x %d Border[b]: %d", w, h, TexBorder); 322 PrintString(s); 323 324 glRasterPos2i(10, 65); 325 sprintf(s, "Format[f]: %s Type: %s IntFormat: %s", 326 FormatStr(FormatTable[Format].Format), 327 TypeStr( FormatTable[Format].Type), 328 FormatStr(FormatTable[Format].IntFormat)); 329 PrintString(s); 330 331 glRasterPos2i(10, 50); 332 sprintf(s, "Pixel Scale&Bias[p]: %s TexSubImage[s]: %s", 333 ScaleAndBias ? "Yes" : "No", 334 SubImage ? "Yes" : "No"); 335 PrintString(s); 336 337 if (Mode == 0) { 338 glRasterPos2i(200, 10); 339 sprintf(s, "...Measuring..."); 340 PrintString(s); 341 glutSwapBuffers(); 342 glutPostRedisplay(); 343 Mode++; 344 } 345 else if (Mode == 1) { 346 MeasureDownloadRate(); 347 glutPostRedisplay(); 348 Mode++; 349 } 350 else { 351 /* show results */ 352 glRasterPos2i(10, 10); 353 sprintf(s, "Download rate: %g Mtexels/second %g MB/second", 354 DownloadRate / 1000000.0, 355 DownloadRate * BytesPerTexel(Format) / 1000000.0); 356 PrintString(s); 357 { 358 GLint r, g, b, a, l, i; 359 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_RED_SIZE, &r); 360 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_GREEN_SIZE, &g); 361 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BLUE_SIZE, &b); 362 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &a); 363 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_LUMINANCE_SIZE, &l); 364 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTENSITY_SIZE, &i); 365 sprintf(s, "TexelBits: R=%d G=%d B=%d A=%d L=%d I=%d", r, g, b, a, l, i); 366 glRasterPos2i(10, 25); 367 PrintString(s); 368 } 369 370 glutSwapBuffers(); 371 } 372} 373 374 375static void 376Reshape(int width, int height) 377{ 378 glViewport( 0, 0, width, height ); 379 glMatrixMode( GL_PROJECTION ); 380 glLoadIdentity(); 381 glOrtho(0, width, 0, height, -1, 1); 382 glMatrixMode( GL_MODELVIEW ); 383 glLoadIdentity(); 384} 385 386 387 388static void 389Key(unsigned char key, int x, int y) 390{ 391 (void) x; 392 (void) y; 393 switch (key) { 394 case ' ': 395 Mode = 0; 396 break; 397 case 'b': 398 /* toggle border */ 399 TexBorder = 1 - TexBorder; 400 Mode = 0; 401 break; 402 case 'f': 403 /* change format */ 404 Format = (Format + 1) % NUM_FORMATS; 405 Mode = 0; 406 break; 407 case 'F': 408 /* change format */ 409 Format = (Format - 1) % NUM_FORMATS; 410 Mode = 0; 411 break; 412 case 'p': 413 /* toggle border */ 414 ScaleAndBias = !ScaleAndBias; 415 Mode = 0; 416 break; 417 case 's': 418 SubImage = !SubImage; 419 Mode = 0; 420 break; 421 case 27: 422 exit(0); 423 break; 424 } 425 glutPostRedisplay(); 426} 427 428 429static void 430SpecialKey(int key, int x, int y) 431{ 432 (void) x; 433 (void) y; 434 switch (key) { 435 case GLUT_KEY_UP: 436 if (TexHeight < MaxSize) 437 TexHeight *= 2; 438 break; 439 case GLUT_KEY_DOWN: 440 if (TexHeight > 1) 441 TexHeight /= 2; 442 break; 443 case GLUT_KEY_LEFT: 444 if (TexWidth > 1) 445 TexWidth /= 2; 446 break; 447 case GLUT_KEY_RIGHT: 448 if (TexWidth < MaxSize) 449 TexWidth *= 2; 450 break; 451 } 452 Mode = 0; 453 glutPostRedisplay(); 454} 455 456 457static void 458Init(void) 459{ 460 printf("GL_VENDOR = %s\n", (const char *) glGetString(GL_VENDOR)); 461 printf("GL_VERSION = %s\n", (const char *) glGetString(GL_VERSION)); 462 printf("GL_RENDERER = %s\n", (const char *) glGetString(GL_RENDERER)); 463} 464 465 466int 467main(int argc, char *argv[]) 468{ 469 glutInit( &argc, argv ); 470 glutInitWindowPosition( 0, 0 ); 471 glutInitWindowSize( 600, 100 ); 472 glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); 473 glutCreateWindow(argv[0]); 474 glutReshapeFunc( Reshape ); 475 glutKeyboardFunc( Key ); 476 glutSpecialFunc( SpecialKey ); 477 glutDisplayFunc( Display ); 478 Init(); 479 glutMainLoop(); 480 return 0; 481} 482