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
;
165 GLubyte
*texImage
, *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
) {
182 printf("alloc %p %p\n", orig_texImage
, orig_getImage
);
184 texImage
= (GLubyte
*)align((unsigned long)orig_texImage
, ALIGN
);
185 getImage
= (GLubyte
*)align((unsigned long)orig_getImage
, ALIGN
);
187 for (i
= 1; !(((unsigned long)texImage
) & i
); i
<<=1)
189 printf("texture image alignment: %d bytes (%p)\n", i
, texImage
);
191 for (i
= 0; i
< bytes
; i
++) {
192 texImage
[i
] = i
& 0xff;
195 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
196 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
199 glPixelTransferf(GL_RED_SCALE
, 0.5);
200 glPixelTransferf(GL_GREEN_SCALE
, 0.5);
201 glPixelTransferf(GL_BLUE_SCALE
, 0.5);
202 glPixelTransferf(GL_RED_BIAS
, 0.5);
203 glPixelTransferf(GL_GREEN_BIAS
, 0.5);
204 glPixelTransferf(GL_BLUE_BIAS
, 0.5);
207 glPixelTransferf(GL_RED_SCALE
, 1.0);
208 glPixelTransferf(GL_GREEN_SCALE
, 1.0);
209 glPixelTransferf(GL_BLUE_SCALE
, 1.0);
210 glPixelTransferf(GL_RED_BIAS
, 0.0);
211 glPixelTransferf(GL_GREEN_BIAS
, 0.0);
212 glPixelTransferf(GL_BLUE_BIAS
, 0.0);
215 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
216 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
217 glEnable(GL_TEXTURE_2D
);
220 t0
= glutGet(GLUT_ELAPSED_TIME
) * 0.001;
222 int img
= count
%NR_TEXOBJ
;
223 GLubyte
*img_ptr
= texImage
+ img
* image_bytes
;
225 glBindTexture(GL_TEXTURE_2D
, TexObj
[img
]);
227 if (SubImage
&& count
> 0) {
228 /* Only update a portion of the image each iteration. This
229 * is presumably why you'd want to use texsubimage, otherwise
230 * you may as well just call teximage again.
232 * A bigger question is whether to use a pointer that moves
233 * with each call, ie does the incoming data come from L2
234 * cache under normal circumstances, or is it pulled from
237 * There's a good argument to say L2 cache, ie you'd expect
238 * the data to have been recently generated. It's possible
239 * that it could have come from a file read, which may or may
240 * not have gone through the cpu.
242 glTexSubImage2D(GL_TEXTURE_2D
, 0,
244 -TexBorder
+ offset
* h
/8,
247 FormatTable
[Format
].Format
,
248 FormatTable
[Format
].Type
,
250 texImage
/* likely in L2$ */
252 img_ptr
+ offset
* bytes
/8 /* unlikely in L2$ */
260 glTexImage2D(GL_TEXTURE_2D
, 0,
261 FormatTable
[Format
].IntFormat
, w
, h
, TexBorder
,
262 FormatTable
[Format
].Format
,
263 FormatTable
[Format
].Type
,
268 /* draw a tiny polygon to force texture into texram */
269 glBegin(GL_TRIANGLES
);
270 glTexCoord2f(0, 0); glVertex2f(1, 1);
271 glTexCoord2f(1, 0); glVertex2f(3, 1);
272 glTexCoord2f(0.5, 1); glVertex2f(2, 3);
275 t1
= glutGet(GLUT_ELAPSED_TIME
) * 0.001;
278 } while (time
< 3.0);
280 glDisable(GL_TEXTURE_2D
);
282 printf("total texels=%f time=%f\n", total
, time
);
283 DownloadRate
= total
/ time
;
290 GLint err
= glGetError();
292 printf("GL error %d\n", err
);
298 PrintString(const char *s
)
301 glutBitmapCharacter(GLUT_BITMAP_8_BY_13
, (int) *s
);
310 const int w
= TexWidth
+ 2 * TexBorder
;
311 const int h
= TexHeight
+ 2 * TexBorder
;
314 glClear(GL_COLOR_BUFFER_BIT
);
316 glRasterPos2i(10, 80);
317 sprintf(s
, "Texture size[cursor]: %d x %d Border[b]: %d", w
, h
, TexBorder
);
320 glRasterPos2i(10, 65);
321 sprintf(s
, "Format[f]: %s Type: %s IntFormat: %s",
322 FormatStr(FormatTable
[Format
].Format
),
323 TypeStr( FormatTable
[Format
].Type
),
324 FormatStr(FormatTable
[Format
].IntFormat
));
327 glRasterPos2i(10, 50);
328 sprintf(s
, "Pixel Scale&Bias[p]: %s TexSubImage[s]: %s",
329 ScaleAndBias
? "Yes" : "No",
330 SubImage
? "Yes" : "No");
334 glRasterPos2i(200, 10);
335 sprintf(s
, "...Measuring...");
341 else if (Mode
== 1) {
342 MeasureDownloadRate();
348 glRasterPos2i(10, 10);
349 sprintf(s
, "Download rate: %g Mtexels/second %g MB/second",
350 DownloadRate
/ 1000000.0,
351 DownloadRate
* BytesPerTexel(Format
) / 1000000.0);
354 GLint r
, g
, b
, a
, l
, i
;
355 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_RED_SIZE
, &r
);
356 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_GREEN_SIZE
, &g
);
357 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_BLUE_SIZE
, &b
);
358 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_ALPHA_SIZE
, &a
);
359 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_LUMINANCE_SIZE
, &l
);
360 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_INTENSITY_SIZE
, &i
);
361 sprintf(s
, "TexelBits: R=%d G=%d B=%d A=%d L=%d I=%d", r
, g
, b
, a
, l
, i
);
362 glRasterPos2i(10, 25);
372 Reshape(int width
, int height
)
374 glViewport( 0, 0, width
, height
);
375 glMatrixMode( GL_PROJECTION
);
377 glOrtho(0, width
, 0, height
, -1, 1);
378 glMatrixMode( GL_MODELVIEW
);
385 Key(unsigned char key
, int x
, int y
)
395 TexBorder
= 1 - TexBorder
;
400 Format
= (Format
+ 1) % NUM_FORMATS
;
405 Format
= (Format
- 1) % NUM_FORMATS
;
410 ScaleAndBias
= !ScaleAndBias
;
414 SubImage
= !SubImage
;
426 SpecialKey(int key
, int x
, int y
)
432 if (TexHeight
< MaxSize
)
444 if (TexWidth
< MaxSize
)
456 printf("GL_VENDOR = %s\n", (const char *) glGetString(GL_VENDOR
));
457 printf("GL_VERSION = %s\n", (const char *) glGetString(GL_VERSION
));
458 printf("GL_RENDERER = %s\n", (const char *) glGetString(GL_RENDERER
));
463 main(int argc
, char *argv
[])
465 glutInit( &argc
, argv
);
466 glutInitWindowPosition( 0, 0 );
467 glutInitWindowSize( 600, 100 );
468 glutInitDisplayMode( GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
);
469 glutCreateWindow(argv
[0]);
470 glutReshapeFunc( Reshape
);
471 glutKeyboardFunc( Key
);
472 glutSpecialFunc( SpecialKey
);
473 glutDisplayFunc( Display
);