3 * Copyright (C) 1999 Brian Paul All Rights Reserved.
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:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
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.
27 * Measure texture download speed.
28 * Use keyboard to change texture size, format, datatype, scale/bias,
31 * Brian Paul 28 January 2000
41 static GLsizei MaxSize
= 2048;
42 static GLsizei TexWidth
= 1024, TexHeight
= 1024, TexBorder
= 0;
43 static GLboolean ScaleAndBias
= GL_FALSE
;
44 static GLboolean SubImage
= GL_FALSE
;
45 static GLdouble DownloadRate
= 0.0; /* texels/sec */
47 static GLuint Mode
= 0;
50 /* Try and avoid L2 cache effects by cycling through a small number of
53 * At the initial size of 1024x1024x4 == 4mbyte, say 8 textures will
54 * keep us out of most caches at 32mb total.
56 * This turns into a fairly interesting question of what exactly you
57 * expect to be in cache in normal usage, and what you think should be
58 * outside. There's no rules for this, no reason to favour one usage
59 * over another except what the application you care about happens to
60 * resemble most closely.
62 * - Should the client texture image be in L2 cache? Has it just been
63 * generated or read from disk?
64 * - Does the application really use >1 texture, or is it constantly
65 * updating one image in-place?
67 * Different answers will favour different texture upload mechanisms.
68 * To upload an image that is purely outside of cache, a DMA-based
69 * upload will probably win, whereas for small, in-cache textures,
73 static GLuint TexObj
[NR_TEXOBJ
];
84 static const struct FormatRec FormatTable
[] = {
85 /* Format Type IntFormat TexelSize */
86 { GL_BGRA
, GL_UNSIGNED_BYTE
, GL_RGBA
, 4 },
87 { GL_RGB
, GL_UNSIGNED_BYTE
, GL_RGB
, 3 },
88 { GL_RGBA
, GL_UNSIGNED_BYTE
, GL_RGBA
, 4 },
89 { GL_RGBA
, GL_UNSIGNED_BYTE
, GL_RGB
, 4 },
90 { GL_RGB
, GL_UNSIGNED_SHORT_5_6_5
, GL_RGB
, 2 },
91 { GL_LUMINANCE
, GL_UNSIGNED_BYTE
, GL_LUMINANCE
, 1 },
92 { GL_LUMINANCE_ALPHA
, GL_UNSIGNED_BYTE
, GL_LUMINANCE_ALPHA
, 2 },
93 { GL_ALPHA
, GL_UNSIGNED_BYTE
, GL_ALPHA
, 1 },
97 #define NUM_FORMATS (sizeof(FormatTable)/sizeof(FormatTable[0]))
100 BytesPerTexel(GLint format
)
102 return FormatTable
[format
].TexelSize
;
107 FormatStr(GLenum format
)
117 return "GL_LUMINANCE";
118 case GL_LUMINANCE_ALPHA
:
119 return "GL_LUMINANCE_ALPHA";
132 case GL_UNSIGNED_BYTE
:
133 return "GL_UNSIGNED_BYTE";
134 case GL_UNSIGNED_SHORT
:
135 return "GL_UNSIGNED_SHORT";
136 case GL_UNSIGNED_SHORT_5_6_5
:
137 return "GL_UNSIGNED_SHORT_5_6_5";
138 case GL_UNSIGNED_SHORT_5_6_5_REV
:
139 return "GL_UNSIGNED_SHORT_5_6_5_REV";
145 /* On x86, there is a performance cliff for memcpy to texture memory
146 * for sources below 64 byte alignment. We do our best with this in
147 * the driver, but it is better if the images are correctly aligned to
150 #define ALIGN (1<<12)
152 static unsigned align(unsigned value
, unsigned a
)
154 return (value
+ a
- 1) & ~(a
-1);
157 static int MIN2(int a
, int b
)
159 return a
< b
? a
: b
;
163 MeasureDownloadRate(void)
165 const int w
= TexWidth
+ 2 * TexBorder
;
166 const int h
= TexHeight
+ 2 * TexBorder
;
167 const int image_bytes
= align(w
* h
* BytesPerTexel(Format
), ALIGN
);
168 const int bytes
= image_bytes
* NR_TEXOBJ
;
169 GLubyte
*orig_texImage
, *orig_getImage
;
170 GLubyte
*texImage
, *getImage
;
171 GLdouble t0
, t1
, time
;
175 GLdouble total
= 0; /* ints will tend to overflow */
177 printf("allocating %d bytes for %d %dx%d images\n",
178 bytes
, NR_TEXOBJ
, w
, h
);
180 orig_texImage
= (GLubyte
*) malloc(bytes
+ ALIGN
);
181 orig_getImage
= (GLubyte
*) malloc(image_bytes
+ ALIGN
);
182 if (!orig_texImage
|| !orig_getImage
) {
187 printf("alloc %p %p\n", orig_texImage
, orig_getImage
);
189 texImage
= (GLubyte
*)align((unsigned)orig_texImage
, ALIGN
);
190 getImage
= (GLubyte
*)align((unsigned)orig_getImage
, ALIGN
);
192 for (i
= 1; !(((unsigned)texImage
) & i
); i
<<=1)
194 printf("texture image alignment: %d bytes (%p)\n", i
, texImage
);
196 for (i
= 0; i
< bytes
; i
++) {
197 texImage
[i
] = i
& 0xff;
200 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
201 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
204 glPixelTransferf(GL_RED_SCALE
, 0.5);
205 glPixelTransferf(GL_GREEN_SCALE
, 0.5);
206 glPixelTransferf(GL_BLUE_SCALE
, 0.5);
207 glPixelTransferf(GL_RED_BIAS
, 0.5);
208 glPixelTransferf(GL_GREEN_BIAS
, 0.5);
209 glPixelTransferf(GL_BLUE_BIAS
, 0.5);
212 glPixelTransferf(GL_RED_SCALE
, 1.0);
213 glPixelTransferf(GL_GREEN_SCALE
, 1.0);
214 glPixelTransferf(GL_BLUE_SCALE
, 1.0);
215 glPixelTransferf(GL_RED_BIAS
, 0.0);
216 glPixelTransferf(GL_GREEN_BIAS
, 0.0);
217 glPixelTransferf(GL_BLUE_BIAS
, 0.0);
220 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
221 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
222 glEnable(GL_TEXTURE_2D
);
225 t0
= glutGet(GLUT_ELAPSED_TIME
) * 0.001;
227 int img
= count
%NR_TEXOBJ
;
228 GLubyte
*img_ptr
= texImage
+ img
* image_bytes
;
230 glBindTexture(GL_TEXTURE_2D
, TexObj
[img
]);
232 if (SubImage
&& count
> 0) {
233 /* Only update a portion of the image each iteration. This
234 * is presumably why you'd want to use texsubimage, otherwise
235 * you may as well just call teximage again.
237 * A bigger question is whether to use a pointer that moves
238 * with each call, ie does the incoming data come from L2
239 * cache under normal circumstances, or is it pulled from
242 * There's a good argument to say L2 cache, ie you'd expect
243 * the data to have been recently generated. It's possible
244 * that it could have come from a file read, which may or may
245 * not have gone through the cpu.
247 glTexSubImage2D(GL_TEXTURE_2D
, 0,
249 -TexBorder
+ offset
* h
/8,
252 FormatTable
[Format
].Format
,
253 FormatTable
[Format
].Type
,
255 texImage
/* likely in L2$ */
257 img_ptr
+ offset
* bytes
/8 /* unlikely in L2$ */
265 glTexImage2D(GL_TEXTURE_2D
, 0,
266 FormatTable
[Format
].IntFormat
, w
, h
, TexBorder
,
267 FormatTable
[Format
].Format
,
268 FormatTable
[Format
].Type
,
273 /* draw a tiny polygon to force texture into texram */
274 glBegin(GL_TRIANGLES
);
275 glTexCoord2f(0, 0); glVertex2f(1, 1);
276 glTexCoord2f(1, 0); glVertex2f(3, 1);
277 glTexCoord2f(0.5, 1); glVertex2f(2, 3);
280 t1
= glutGet(GLUT_ELAPSED_TIME
) * 0.001;
283 } while (time
< 3.0);
285 glDisable(GL_TEXTURE_2D
);
287 printf("total texels=%f time=%f\n", total
, time
);
288 DownloadRate
= total
/ time
;
295 GLint err
= glGetError();
297 printf("GL error %d\n", err
);
303 PrintString(const char *s
)
306 glutBitmapCharacter(GLUT_BITMAP_8_BY_13
, (int) *s
);
315 const int w
= TexWidth
+ 2 * TexBorder
;
316 const int h
= TexHeight
+ 2 * TexBorder
;
319 glClear(GL_COLOR_BUFFER_BIT
);
321 glRasterPos2i(10, 80);
322 sprintf(s
, "Texture size[cursor]: %d x %d Border[b]: %d", w
, h
, TexBorder
);
325 glRasterPos2i(10, 65);
326 sprintf(s
, "Format[f]: %s Type: %s IntFormat: %s",
327 FormatStr(FormatTable
[Format
].Format
),
328 TypeStr( FormatTable
[Format
].Type
),
329 FormatStr(FormatTable
[Format
].IntFormat
));
332 glRasterPos2i(10, 50);
333 sprintf(s
, "Pixel Scale&Bias[p]: %s TexSubImage[s]: %s",
334 ScaleAndBias
? "Yes" : "No",
335 SubImage
? "Yes" : "No");
339 glRasterPos2i(200, 10);
340 sprintf(s
, "...Measuring...");
346 else if (Mode
== 1) {
347 MeasureDownloadRate();
353 glRasterPos2i(10, 10);
354 sprintf(s
, "Download rate: %g Mtexels/second %g MB/second",
355 DownloadRate
/ 1000000.0,
356 DownloadRate
* BytesPerTexel(Format
) / 1000000.0);
359 GLint r
, g
, b
, a
, l
, i
;
360 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_RED_SIZE
, &r
);
361 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_GREEN_SIZE
, &g
);
362 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_BLUE_SIZE
, &b
);
363 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_ALPHA_SIZE
, &a
);
364 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_LUMINANCE_SIZE
, &l
);
365 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_INTENSITY_SIZE
, &i
);
366 sprintf(s
, "TexelBits: R=%d G=%d B=%d A=%d L=%d I=%d", r
, g
, b
, a
, l
, i
);
367 glRasterPos2i(10, 25);
377 Reshape(int width
, int height
)
379 glViewport( 0, 0, width
, height
);
380 glMatrixMode( GL_PROJECTION
);
382 glOrtho(0, width
, 0, height
, -1, 1);
383 glMatrixMode( GL_MODELVIEW
);
390 Key(unsigned char key
, int x
, int y
)
400 TexBorder
= 1 - TexBorder
;
405 Format
= (Format
+ 1) % NUM_FORMATS
;
410 Format
= (Format
- 1) % NUM_FORMATS
;
415 ScaleAndBias
= !ScaleAndBias
;
419 SubImage
= !SubImage
;
431 SpecialKey(int key
, int x
, int y
)
437 if (TexHeight
< MaxSize
)
449 if (TexWidth
< MaxSize
)
461 printf("GL_VENDOR = %s\n", (const char *) glGetString(GL_VENDOR
));
462 printf("GL_VERSION = %s\n", (const char *) glGetString(GL_VERSION
));
463 printf("GL_RENDERER = %s\n", (const char *) glGetString(GL_RENDERER
));
468 main(int argc
, char *argv
[])
470 glutInit( &argc
, argv
);
471 glutInitWindowPosition( 0, 0 );
472 glutInitWindowSize( 600, 100 );
473 glutInitDisplayMode( GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
);
474 glutCreateWindow(argv
[0]);
475 glutReshapeFunc( Reshape
);
476 glutKeyboardFunc( Key
);
477 glutSpecialFunc( SpecialKey
);
478 glutDisplayFunc( Display
);