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 long align(unsigned long value
, unsigned long a
)
154 return (value
+ a
- 1) & ~(a
-1);
158 MeasureDownloadRate(void)
160 const int w
= TexWidth
+ 2 * TexBorder
;
161 const int h
= TexHeight
+ 2 * TexBorder
;
162 const int image_bytes
= align(w
* h
* BytesPerTexel(Format
), ALIGN
);
163 const int bytes
= image_bytes
* NR_TEXOBJ
;
164 GLubyte
*orig_texImage
, *orig_getImage
;
166 GLdouble t0
, t1
, time
;
170 GLdouble total
= 0; /* ints will tend to overflow */
172 printf("allocating %d bytes for %d %dx%d images\n",
173 bytes
, NR_TEXOBJ
, w
, h
);
175 orig_texImage
= (GLubyte
*) malloc(bytes
+ ALIGN
);
176 orig_getImage
= (GLubyte
*) malloc(image_bytes
+ ALIGN
);
177 if (!orig_texImage
|| !orig_getImage
) {
184 printf("alloc %p %p\n", orig_texImage
, orig_getImage
);
186 texImage
= (GLubyte
*)align((unsigned long)orig_texImage
, ALIGN
);
188 for (i
= 1; !(((unsigned long)texImage
) & i
); i
<<=1)
190 printf("texture image alignment: %d bytes (%p)\n", i
, texImage
);
192 for (i
= 0; i
< bytes
; i
++) {
193 texImage
[i
] = i
& 0xff;
196 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
197 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
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);
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);
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
);
221 t0
= glutGet(GLUT_ELAPSED_TIME
) * 0.001;
223 int img
= count
%NR_TEXOBJ
;
224 GLubyte
*img_ptr
= texImage
+ img
* image_bytes
;
226 glBindTexture(GL_TEXTURE_2D
, TexObj
[img
]);
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.
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
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.
243 glTexSubImage2D(GL_TEXTURE_2D
, 0,
245 -TexBorder
+ offset
* h
/8,
248 FormatTable
[Format
].Format
,
249 FormatTable
[Format
].Type
,
251 texImage
/* likely in L2$ */
253 img_ptr
+ offset
* bytes
/8 /* unlikely in L2$ */
261 glTexImage2D(GL_TEXTURE_2D
, 0,
262 FormatTable
[Format
].IntFormat
, w
, h
, TexBorder
,
263 FormatTable
[Format
].Format
,
264 FormatTable
[Format
].Type
,
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);
276 t1
= glutGet(GLUT_ELAPSED_TIME
) * 0.001;
279 } while (time
< 3.0);
281 glDisable(GL_TEXTURE_2D
);
283 printf("total texels=%f time=%f\n", total
, time
);
284 DownloadRate
= total
/ time
;
291 GLint err
= glGetError();
293 printf("GL error %d\n", err
);
299 PrintString(const char *s
)
302 glutBitmapCharacter(GLUT_BITMAP_8_BY_13
, (int) *s
);
311 const int w
= TexWidth
+ 2 * TexBorder
;
312 const int h
= TexHeight
+ 2 * TexBorder
;
315 glClear(GL_COLOR_BUFFER_BIT
);
317 glRasterPos2i(10, 80);
318 sprintf(s
, "Texture size[cursor]: %d x %d Border[b]: %d", w
, h
, TexBorder
);
321 glRasterPos2i(10, 65);
322 sprintf(s
, "Format[f]: %s Type: %s IntFormat: %s",
323 FormatStr(FormatTable
[Format
].Format
),
324 TypeStr( FormatTable
[Format
].Type
),
325 FormatStr(FormatTable
[Format
].IntFormat
));
328 glRasterPos2i(10, 50);
329 sprintf(s
, "Pixel Scale&Bias[p]: %s TexSubImage[s]: %s",
330 ScaleAndBias
? "Yes" : "No",
331 SubImage
? "Yes" : "No");
335 glRasterPos2i(200, 10);
336 sprintf(s
, "...Measuring...");
342 else if (Mode
== 1) {
343 MeasureDownloadRate();
349 glRasterPos2i(10, 10);
350 sprintf(s
, "Download rate: %g Mtexels/second %g MB/second",
351 DownloadRate
/ 1000000.0,
352 DownloadRate
* BytesPerTexel(Format
) / 1000000.0);
355 GLint r
, g
, b
, a
, l
, i
;
356 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_RED_SIZE
, &r
);
357 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_GREEN_SIZE
, &g
);
358 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_BLUE_SIZE
, &b
);
359 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_ALPHA_SIZE
, &a
);
360 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_LUMINANCE_SIZE
, &l
);
361 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_INTENSITY_SIZE
, &i
);
362 sprintf(s
, "TexelBits: R=%d G=%d B=%d A=%d L=%d I=%d", r
, g
, b
, a
, l
, i
);
363 glRasterPos2i(10, 25);
373 Reshape(int width
, int height
)
375 glViewport( 0, 0, width
, height
);
376 glMatrixMode( GL_PROJECTION
);
378 glOrtho(0, width
, 0, height
, -1, 1);
379 glMatrixMode( GL_MODELVIEW
);
386 Key(unsigned char key
, int x
, int y
)
396 TexBorder
= 1 - TexBorder
;
401 Format
= (Format
+ 1) % NUM_FORMATS
;
406 Format
= (Format
- 1) % NUM_FORMATS
;
411 ScaleAndBias
= !ScaleAndBias
;
415 SubImage
= !SubImage
;
427 SpecialKey(int key
, int x
, int y
)
433 if (TexHeight
< MaxSize
)
445 if (TexWidth
< MaxSize
)
457 printf("GL_VENDOR = %s\n", (const char *) glGetString(GL_VENDOR
));
458 printf("GL_VERSION = %s\n", (const char *) glGetString(GL_VERSION
));
459 printf("GL_RENDERER = %s\n", (const char *) glGetString(GL_RENDERER
));
464 main(int argc
, char *argv
[])
466 glutInit( &argc
, argv
);
467 glutInitWindowPosition( 0, 0 );
468 glutInitWindowSize( 600, 100 );
469 glutInitDisplayMode( GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
);
470 glutCreateWindow(argv
[0]);
471 glutReshapeFunc( Reshape
);
472 glutKeyboardFunc( Key
);
473 glutSpecialFunc( SpecialKey
);
474 glutDisplayFunc( Display
);