[Mesa-dev,demos,v2,2/3] perf: Update teximage to measure more formats

Submitted by Courtney Goeltzenleuchter on Nov. 12, 2013, 12:53 a.m.

Details

Message ID 1384217610-10874-3-git-send-email-courtney@LunarG.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Courtney Goeltzenleuchter Nov. 12, 2013, 12:53 a.m.
Needed test to measure texture upload speed under a variety
of modes (mipmap, source format, internal format, size, etc.)
This new test has an interactive run mode like the other Mesa
Perf tests but also includes command line options to make
it automatable.
Fix up code formatting.
Integrate review feedback.

This provides a quick way to get feedback on texture upload
related performance tuning.
Texture image data is initialized and aligned to 64 byte bounary.
Uses Mesa demos Perf library to do the measurements.

Signed-off-by: Courtney Goeltzenleuchter <courtney@LunarG.com>
---
 src/perf/teximage.c | 720 ++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 563 insertions(+), 157 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/perf/teximage.c b/src/perf/teximage.c
index 88316f3..3ab33a5 100644
--- a/src/perf/teximage.c
+++ b/src/perf/teximage.c
@@ -1,5 +1,6 @@ 
 /*
  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
+ * Copyright (C) 2012-2013 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -14,49 +15,64 @@ 
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * VMWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
  */
 
 /**
- * Measure glTex[Sub]Image2D() and glGetTexImage() rate
- *
- * Brian Paul
- * 16 Sep 2009
+ * Measure glTexImage2D() rate
+ *   enhanced from teximage.c by Brian Paul, 16 Sep 2009
+ *      2013 - Lisa Owens (lisa at lunarg.com)
+ *           - Courtney Goeltzenleuchter (courtney at lunarg.com)
  */
 
 #include "glmain.h"
 #include "common.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
 
+/**
+ * Align a value up to an alignment value
+ *
+ * If \c value is not already aligned to the requested alignment value, it
+ * will be rounded up.
+ *
+ * \param value  Value to be rounded
+ * \param alignment  Alignment value to be used.  This must be a power of two.
+ *
+ * \sa ROUND_DOWN_TO()
+ */
+#define ALIGN_PTR(value, alignment)  (((uintptr_t)(value) + (alignment) - 1) & ~ (uintptr_t)((alignment) - 1))
 
 int WinWidth = 100, WinHeight = 100;
 
+/* for texture creation */
 static GLuint VBO;
 static GLuint TexObj = 0;
 static GLubyte *TexImage = NULL;
-static GLsizei TexSize;
-static GLenum TexIntFormat, TexSrcFormat, TexSrcType;
-
-static const GLboolean DrawPoint = GL_TRUE;
-static const GLboolean TexSubImage4 = GL_FALSE;
 
 enum {
-   MODE_CREATE_TEXIMAGE,
-   MODE_TEXIMAGE,
-   MODE_TEXSUBIMAGE,
-   MODE_GETTEXIMAGE,
-   MODE_COUNT
+   TEST_CREATE_TEXIMAGE,
+   TEST_TEXIMAGE,
+   TEST_TEXIMAGE_MIPMAP,
+   TEST_TEXSUBIMAGE,
+   TEST_GETTEXIMAGE,
+   TEST_COUNT
 };
 
-static const char *mode_name[MODE_COUNT] = 
-{
+static const char *test_name[TEST_COUNT] = {
    "Create_TexImage",
    "TexImage",
+   "TexImage_Mipmap",
    "TexSubImage",
    "GetTexImage"
 };
 
+GLboolean test_enable[TEST_COUNT];
 
 
 struct vertex
@@ -68,8 +84,322 @@  static const struct vertex vertices[1] = {
    { 0.0, 0.0, 0.5, 0.5 },
 };
 
+
+/** define some functions */
+GLenum parseType(char *);
+GLenum parseFormat(char *, int);
+GLuint determineTexelSize(GLenum format, GLenum type);
+void parseConfigFile(void);
+GLuint GenerateMipmapImages(void);
+
 #define VOFFSET(F) ((void *) offsetof(struct vertex, F))
 
+/** defaults; options can be provided to set these */
+GLint g_level = 0;
+GLsizei g_width = 256;
+GLsizei g_height = 256;
+GLuint g_texelsize = 4;
+GLenum g_texsrctype = GL_UNSIGNED_BYTE;
+GLenum g_texfmt = GL_RGBA;
+GLenum g_texintfmt = 0;
+GLboolean g_drawpoint = GL_TRUE;
+GLboolean g_subtexquad = GL_FALSE;
+char configName[2056];
+int formattype = 1;
+int intformattype = 2;
+char typeName[256] = "GL_UNSIGNED_BYTE";
+char formatName[256] = "GL_RGBA";
+char internalformatName[256] = "GL_RGBA";
+GLboolean g_verbose = GL_FALSE;
+
+/* used when mipmapping */
+static GLsizei g_initialwidth;
+static GLsizei g_initialheight;
+int g_numLevel = 0;
+GLubyte *g_mipmapimgs[64];
+GLsizei g_mmwidths[64];
+GLsizei g_mmheights[64];
+
+enum csvStyle {
+   CSV_STYLE_OFF,
+   CSV_STYLE_DATA,
+   CSV_STYLE_FULL
+};
+enum csvStyle g_csvstyle = CSV_STYLE_OFF;
+
+struct gl_parse_type {
+   char *name;
+   GLenum type;
+   bool fixed_size; /* true indicates texel size is component size */
+   int component_size;
+};
+
+struct gl_parse_type parse_type_table[] = {
+   {"GL_UNSIGNED_BYTE", GL_UNSIGNED_BYTE, false, 1},
+   {"GL_BYTE", GL_BYTE, false, 1},
+   {"GL_UNSIGNED_SHORT", GL_UNSIGNED_SHORT, false, 2},
+   {"GL_SHORT", GL_SHORT, false, 2},
+   {"GL_UNSIGNED_INT", GL_UNSIGNED_INT, false, 4},
+   {"GL_INT", GL_INT, false, 4},
+   {"GL_FLOAT", GL_FLOAT, false, 4},
+   {"GL_UNSIGNED_BYTE_3_3_2", GL_UNSIGNED_BYTE_3_3_2, true, 1},
+   {"GL_UNSIGNED_BYTE_2_3_3_REV", GL_UNSIGNED_BYTE_2_3_3_REV, true, 1},
+   {"GL_UNSIGNED_SHORT_5_6_5", GL_UNSIGNED_SHORT_5_6_5, true, 2},
+   {"GL_UNSIGNED_SHORT_5_6_5_REV", GL_UNSIGNED_SHORT_5_6_5_REV, true, 2},
+   {"GL_UNSIGNED_SHORT_4_4_4_4", GL_UNSIGNED_SHORT_4_4_4_4, true, 2},
+   {"GL_UNSIGNED_SHORT_4_4_4_4_REV", GL_UNSIGNED_SHORT_4_4_4_4_REV, true, 2},
+   {"GL_UNSIGNED_SHORT_5_5_5_1", GL_UNSIGNED_SHORT_5_5_5_1, true, 2},
+   {"GL_UNSIGNED_SHORT_1_5_5_5_REV", GL_UNSIGNED_SHORT_1_5_5_5_REV, true, 2},
+   {"GL_UNSIGNED_INT_8_8_8_8", GL_UNSIGNED_INT_8_8_8_8, true, 4},
+   {"GL_UNSIGNED_INT_8_8_8_8_REV", GL_UNSIGNED_INT_8_8_8_8_REV, true, 4},
+   {"GL_UNSIGNED_INT_10_10_10_2", GL_UNSIGNED_INT_10_10_10_2, true, 4},
+   {"GL_UNSIGNED_INT_2_10_10_10_REV", GL_UNSIGNED_INT_2_10_10_10_REV, true, 4},
+};
+
+/** parse the type string */
+GLenum
+parseType(char *typestr)
+{
+   GLenum ogltype;
+   int i;
+
+   for (i=0; i<(sizeof(parse_type_table)/sizeof(struct gl_parse_type)); i++) {
+      if (strcmp(typestr, parse_type_table[i].name) == 0) {
+         strcpy(typeName, typestr);
+         return parse_type_table[i].type;
+      }
+   }
+
+   /* if we get here, type specified is invalid; use a default */
+   perf_printf("\nwarning: type %s unknown; using GL_UNSIGNED_BYTE\n", typestr);
+   ogltype = GL_UNSIGNED_BYTE;
+   strcpy(typeName, "GL_UNSIGNED_BYTE");
+
+   return ogltype;
+}
+
+/** parse the format string */
+GLenum
+parseFormat(char *formatstr, int ftype)
+{
+   GLenum oglformat;
+
+   if (ftype == formattype) {
+      strcpy(formatName, formatstr);
+   } else {
+      strcpy(internalformatName, formatstr);
+   }
+
+   if (strcmp(formatstr, "GL_RGB") == 0) {
+      oglformat = GL_RGB;
+   } else if (strcmp(formatstr, "GL_RGBA") == 0) {
+      oglformat = GL_RGBA;
+   } else if (strcmp(formatstr, "GL_BGRA") == 0) {
+      oglformat = GL_BGRA;
+   } else if (strcmp(formatstr, "GL_LUMINANCE") == 0) {
+      oglformat = GL_LUMINANCE;
+   } else {
+      /* if we get here, format specified is invalid; use a default */
+      perf_printf("\nwarning: format %s unknown; using GL_RGBA\n", formatstr);
+      oglformat = GL_RGB;
+      if (ftype == formattype) {
+         strcpy(formatName, "GL_RGBA");
+      } else {
+         strcpy(internalformatName, "GL_RGBA");
+      }
+   }
+
+   return oglformat;
+}
+
+GLuint
+determineTexelSize(GLenum format, GLenum type)
+{
+   int i, component_size = 1, num_components = 1;
+
+   for (i=0; i<(sizeof(parse_type_table)/sizeof(struct gl_parse_type)); i++) {
+      if (type == parse_type_table[i].type) {
+         if (parse_type_table[i].fixed_size)
+            return parse_type_table[i].component_size;
+
+         component_size = parse_type_table[i].component_size;
+         break;
+      }
+   }
+
+   switch (format)
+   {
+   case GL_RGBA:
+   case GL_BGRA:
+      num_components = 4;
+      break;
+
+   case GL_RGB:
+      num_components = 3;
+      break;
+
+   case GL_LUMINANCE:
+      num_components = 1;
+      break;
+
+   default:
+      num_components = 4;
+   }
+
+   return component_size * num_components;
+}
+
+/** parse the args in the teximage_enh.opts file */
+static void
+parseCommands(int argc, char *argv[])
+{
+   char *key, *value;
+   int intval;
+   int arg, i;
+
+   bzero(test_enable, sizeof (test_enable));
+
+   for (arg = 1; arg < argc; arg++) {
+      /* parse the arguments and set options appropriately */
+      key = argv[arg];
+      if (arg < argc) {
+         /* point at next arg as most options require one. */
+         value = argv[arg + 1];
+      } else {
+         value = "0"; /* default arg */
+      }
+      if (strcmp(key, "--width") == 0) {
+         arg++;
+         if (arg < argc) {
+            intval = atoi(value);
+            if (intval != 0) {
+               g_width = (GLsizei) intval;
+            }
+         }
+      } else if (strcmp(key, "--height") == 0) {
+         if (arg < argc) {
+            arg++;
+            intval = atoi(value);
+            if (intval != 0) {
+               g_height = (GLsizei) intval;
+            }
+         }
+      } else if (strcmp(key, "--level") == 0) {
+         arg++;
+         if (arg < argc) {
+            intval = atoi(value);
+            g_level = (GLint) intval;
+         }
+      } else if (strcmp(key, "--drawpoint") == 0) {
+         /* default is true, so check for false */
+         if (arg < argc) {
+            arg++;
+            if (strcmp(value, "no") == 0) {
+               g_drawpoint = GL_FALSE;
+            } else if (strcmp(value, "yes") == 0) {
+               g_drawpoint = GL_TRUE;
+            }
+         }
+      } else if (strcmp(key, "--type") == 0) {
+         if (arg < argc) {
+            arg++;
+            g_texsrctype = parseType(value);
+         }
+      } else if (strcmp(key, "--format") == 0) {
+         if (arg < argc) {
+            arg++;
+            g_texfmt = parseFormat(value, formattype);
+         }
+      } else if (strcmp(key, "--internalformat") == 0) {
+         if (arg < argc) {
+            arg++;
+            g_texintfmt = parseFormat(value, intformattype);
+         }
+      } else if (strcmp(key, "--test") == 0) {
+         if (arg < argc) {
+            arg++;
+            for (i = 0; i < TEST_COUNT; i++) {
+               if (strcmp(value, test_name[i]) == 0) {
+                  test_enable[i] = GL_TRUE;
+                  break;
+               }
+            }
+            if (i == TEST_COUNT) {
+               perf_printf("Invalid test specified: %s", value);
+               exit(1);
+            }
+         }
+      } else if (strcmp(key, "--verbose") == 0) {
+         g_verbose = GL_TRUE;
+      } else if (strcmp(key, "--csvstyle") == 0) {
+         if (arg < argc) {
+            arg++;
+            if (strcmp(value, "off") == 0) {
+               g_csvstyle = CSV_STYLE_OFF; /* Show standard output */
+            } else if (strcmp(value, "data") == 0) {
+               g_csvstyle = CSV_STYLE_DATA; /* Show only perf stats */
+            } else if (strcmp(value, "full") == 0) {
+               g_csvstyle = CSV_STYLE_FULL; /* show header and perf stats */
+            }
+         }
+      } else if (strcmp(key, "--help") == 0) {
+         perf_printf("run glTexImage2D benchmark\n");
+         perf_printf("Usage: %s [--width <num>] [--height <num>] [--level <num>]\n", argv[0]);
+         perf_printf("                [--type <glenum>] [--texelsize <num>] [--format <glenum>]\n");
+         perf_printf("                [--internalformat <gltype>] [--drawpoint yes/no] [--verbose]\n");
+         perf_printf("                [--csvstyle off|data|full] [--test ");
+         for (i = 0; i < TEST_COUNT; i++) {
+            perf_printf("%s", test_name[i]);
+            /* print separator all but last one */
+            if (i < (TEST_COUNT - 1)) perf_printf(" | ");
+         }
+         perf_printf("]\n");
+         exit(0);
+      } else {
+         perf_printf("Unrecognized argument: %s\n", key);
+         exit(1);
+      }
+   }
+
+   g_texelsize = determineTexelSize(g_texfmt, g_texsrctype);
+
+   for (i = 0; i < TEST_COUNT; i++) {
+      if (test_enable[i] == GL_TRUE)
+         break;
+   }
+   if (i == TEST_COUNT) {
+      /* No tests were enabled, so enable all of them */
+      for (i = 0; i < TEST_COUNT; i++) {
+         test_enable[i] = GL_TRUE;
+      }
+   }
+
+   /* if internal format not provided, set to source format */
+   if (g_texintfmt == 0) {
+      g_texintfmt = g_texfmt;
+   }
+
+   /* generate config name for output */
+   sprintf(configName, "%s/%s/%s", formatName, internalformatName, typeName);
+   if (g_csvstyle == CSV_STYLE_OFF && g_verbose)
+      perf_printf("config name (fmt/intfmt/type): %s\n", configName);
+
+   /* print out values using for this test run */
+   if (g_verbose) {
+      perf_printf("running benchmark with the following options:\n");
+      perf_printf("   type: %s:%d\n", typeName, g_texsrctype);
+      perf_printf("   format: %s:%d\n", formatName, g_texfmt);
+      perf_printf("   internalformat: %s:%d\n", internalformatName, g_texintfmt);
+      perf_printf("   level: %d\n", g_level);
+      perf_printf("   width: %d\n", g_width);
+      perf_printf("   height: %d\n", g_height);
+      perf_printf("   texel size: %d\n", g_texelsize);
+      perf_printf("   draw point?: %d\n", g_drawpoint);
+   }
+
+   return;
+
+}
 
 /** Called from test harness/main */
 void
@@ -91,15 +421,20 @@  PerfInit(int argc, char *argv[])
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glEnable(GL_TEXTURE_2D);
-}
-
-
 
+   /* parse config file */
+   parseCommands(argc, argv);
+}
 
 static void
 CreateUploadTexImage2D(unsigned count)
 {
    unsigned i;
+
+   if (g_verbose) {
+      perf_printf("CreateUploadTexImage2D count: %d\n", count);
+   }
+
    for (i = 0; i < count; i++) {
       if (TexObj)
          glDeleteTextures(1, &TexObj);
@@ -109,68 +444,135 @@  CreateUploadTexImage2D(unsigned count)
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
-      glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
-                   TexSize, TexSize, 0,
-                   TexSrcFormat, TexSrcType, TexImage);
+      glTexImage2D(GL_TEXTURE_2D, g_level, g_texintfmt,
+                   g_width, g_height, 0,
+                   g_texfmt, g_texsrctype, TexImage);
 
-      if (DrawPoint)
+      if (g_drawpoint)
          glDrawArrays(GL_POINTS, 0, 1);
    }
    glFinish();
 }
 
-
 static void
 UploadTexImage2D(unsigned count)
 {
    unsigned i;
+
+   if (g_verbose) {
+      perf_printf("UploadTexImage2D count: %d\n", count);
+   }
+
    for (i = 0; i < count; i++) {
-      /* XXX is this equivalent to a glTexSubImage call since we're
-       * always specifying the same image size?  That case isn't optimized
-       * in Mesa but may be optimized in other drivers.  Note sure how
-       * much difference that might make.
-       */
-      glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
-                   TexSize, TexSize, 0,
-                   TexSrcFormat, TexSrcType, TexImage);
-      if (DrawPoint)
+      glTexImage2D(GL_TEXTURE_2D, g_level, g_texintfmt,
+                   g_width, g_height, 0,
+                   g_texfmt, g_texsrctype, TexImage);
+      if (g_drawpoint)
          glDrawArrays(GL_POINTS, 0, 1);
    }
    glFinish();
 }
 
+GLuint
+GenerateMipmapImages(void)
+{
+   GLuint w, h;
+   GLint bytesPerImage, mallocSize, bytesPerLevel;
+   GLubyte *levelPtr;
+   w = g_width;
+   h = g_height;
+   bytesPerImage = 0;
+   mallocSize = 0;
+
+   /* compute totals first */
+   while ((w > 1) || (h > 1)) {
+      bytesPerLevel = w * h * g_texelsize;
+      bytesPerImage += bytesPerLevel;
+      /* Add 64 so that we can align the data to 64 byte boundary */
+      mallocSize += bytesPerLevel + 0x40;
+
+      if (w > 1) {
+         w = w / 2;
+      }
+      if (h > 1) {
+         h = h / 2;
+      }
+   }
+
+   TexImage = malloc(mallocSize);
+   levelPtr = TexImage;
+
+   w = g_width;
+   h = g_height;
+   /* generate the mipmap images */
+   while ((w > 1) || (h > 1)) {
+      bytesPerLevel = w * h * g_texelsize;
+
+      /* Round to the next higher 64byte boundary.
+       * That is generally better for today's HW and
+       * easy for an app to do if they care.
+       */
+      levelPtr = (GLubyte *) ALIGN_PTR(levelPtr, 64);
+
+      /* set memory to "simulate" loading an image */
+      memset(levelPtr, g_numLevel, bytesPerLevel);
+
+      g_mipmapimgs[g_numLevel] = levelPtr;
+      g_mmwidths[g_numLevel] = w;
+      g_mmheights[g_numLevel] = h;
+      g_numLevel = g_numLevel + 1;
+
+      /* Advance pointer to next free area */
+      levelPtr += bytesPerLevel;
+
+      if (w > 1) {
+         w = w / 2;
+      }
+      if (h > 1) {
+         h = h / 2;
+      }
+   }
+   if (g_verbose) {
+      perf_printf("GenerateMipmapImages: generated %d mipmaps\n", g_numLevel);
+   }
+
+   return bytesPerImage;
+
+}
 
 static void
 UploadTexSubImage2D(unsigned count)
 {
    unsigned i;
    for (i = 0; i < count; i++) {
-      if (TexSubImage4) {
-         GLsizei halfSize = (TexSize == 1) ? 1 : TexSize / 2;
-         GLsizei halfPos = TexSize - halfSize;
+      if (g_subtexquad) {
+         GLsizei halfSizeW = (g_width == 1) ? 1 : g_width / 2;
+         GLsizei halfPosX = g_width - halfSizeW;
+         GLsizei halfSizeH = (g_height == 1) ? 1 : g_height / 2;
+         GLsizei halfPosY = g_height - halfSizeH;
          /* do glTexSubImage2D in four pieces */
          /* lower-left */
-         glPixelStorei(GL_UNPACK_ROW_LENGTH, TexSize);
+         glPixelStorei(GL_UNPACK_ROW_LENGTH, g_width);
          glTexSubImage2D(GL_TEXTURE_2D, 0,
-                         0, 0, halfSize, halfSize,
-                         TexSrcFormat, TexSrcType, TexImage);
+                         0, 0, halfSizeW, halfSizeH,
+                         g_texfmt, g_texsrctype, TexImage);
          /* lower-right */
-         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPos);
+         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPosX);
          glTexSubImage2D(GL_TEXTURE_2D, 0,
-                         halfPos, 0, halfSize, halfSize,
-                         TexSrcFormat, TexSrcType, TexImage);
+                         halfPosX, 0, halfSizeW, halfSizeH,
+                         g_texfmt, g_texsrctype, TexImage);
          /* upper-left */
          glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
-         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPos);
+         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPosY);
          glTexSubImage2D(GL_TEXTURE_2D, 0,
-                         0, halfPos, halfSize, halfSize,
-                         TexSrcFormat, TexSrcType, TexImage);
+                         0, halfPosY, halfSizeW, halfSizeH,
+                         g_texfmt, g_texsrctype, TexImage);
          /* upper-right */
-         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPos);
-         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPos);
+         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPosX);
+         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPosY);
          glTexSubImage2D(GL_TEXTURE_2D, 0,
-                         halfPos, halfPos, halfSize, halfSize,
-                         TexSrcFormat, TexSrcType, TexImage);
+                         halfPosX, halfPosY, halfSizeW, halfSizeH,
+                         g_texfmt, g_texsrctype, TexImage);
          /* reset the unpacking state */
          glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
          glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
@@ -179,46 +581,52 @@  UploadTexSubImage2D(unsigned count)
       else {
          /* replace whole texture image at once */
          glTexSubImage2D(GL_TEXTURE_2D, 0,
-                         0, 0, TexSize, TexSize,
-                         TexSrcFormat, TexSrcType, TexImage);
+                         0, 0, g_width, g_height,
+                         g_texfmt, g_texsrctype, TexImage);
       }
-      if (DrawPoint)
+      if (g_drawpoint)
          glDrawArrays(GL_POINTS, 0, 1);
    }
    glFinish();
 }
 
-
 static void
 GetTexImage2D(unsigned count)
 {
    unsigned i;
-   GLubyte *buf = (GLubyte *) malloc(TexSize * TexSize * 4);
+   GLubyte *buf = (GLubyte *) malloc(g_width * g_height * g_texelsize);
    for (i = 0; i < count; i++) {
       glGetTexImage(GL_TEXTURE_2D, 0,
-                    TexSrcFormat, TexSrcType, buf);
+                    g_texfmt, g_texsrctype, buf);
    }
    glFinish();
    free(buf);
 }
 
+static void
+UploadTexImage2DMipmap(unsigned count)
+{
+   unsigned i, j;
 
-/* XXX any other formats to measure? */
-static const struct {
-   GLenum format, type;
-   GLenum internal_format;
-   const char *name;
-   GLuint texel_size;
-   GLboolean full_test;
-} SrcFormats[] = {
-   { GL_RGBA, GL_UNSIGNED_BYTE,       GL_RGBA, "RGBA/ubyte", 4,   GL_TRUE },
-   { GL_RGB, GL_UNSIGNED_BYTE,        GL_RGB, "RGB/ubyte", 3,     GL_FALSE },
-   { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_RGB, "RGB/565", 2,       GL_FALSE },
-   { GL_BGRA, GL_UNSIGNED_BYTE,       GL_RGBA, "BGRA/ubyte", 4,   GL_FALSE },
-   { GL_LUMINANCE, GL_UNSIGNED_BYTE,  GL_LUMINANCE, "L/ubyte", 1, GL_FALSE },
-   { 0, 0, 0, NULL, 0, 0 }
-};
 
+   /* ?? make this configurable ?? */
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+   /* mipmaps previously generated, call glTexImage2D for all levels */
+   for (i = 0; i < count; i++) {
+      for (j = 0; j < g_numLevel; j++) {
+         if (g_verbose) {
+            perf_printf("mipmap glTexImage2D - level: %d, width: %d, height: %d\n",
+                        j, g_mmwidths[j], g_mmheights[j]);
+         }
+         glTexImage2D(GL_TEXTURE_2D, j, g_texintfmt, g_mmwidths[j],
+                      g_mmheights[j], 0, g_texfmt, g_texsrctype, g_mipmapimgs[j]);
+         if (g_drawpoint)
+            glDrawArrays(GL_POINTS, 0, 1);
+      }
+   }
+   glFinish();
+
+}
 
 /** Called from test harness/main */
 void
@@ -226,104 +634,102 @@  PerfNextRound(void)
 {
 }
 
-
 /** Called from test harness/main */
 void
 PerfDraw(void)
 {
    GLint maxSize;
    double rate;
-   GLint fmt, mode;
+   GLint test;
+   double mbPerSec; // Megabytes per second
+   double mTPerSec; // MegaTexels per second
+   GLint bytesPerImage;
+
+   if (g_csvstyle == CSV_STYLE_FULL) {
+      perf_printf("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n",
+                  "ID", "Test", "width", "height", "level", "type",
+                  "texelsize", "format", "internalformat",
+                  "images/sec", "MB/sec", "MT/sec");
+   }
 
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+   /* TODO - check for max size exceeded?? */
+
+   /* loop over glTexImage */
+   for (test = 0; test < TEST_COUNT; test++) {
+
+      if (test_enable[test] == GL_FALSE) continue;
+
+      switch (test) {
+      case TEST_TEXIMAGE:
+         bytesPerImage = g_width * g_height * g_texelsize;
+         TexImage = malloc(bytesPerImage);
+         rate = PerfMeasureRate(UploadTexImage2D);
+         break;
+
+      case TEST_CREATE_TEXIMAGE:
+         bytesPerImage = g_width * g_height * g_texelsize;
+         TexImage = malloc(bytesPerImage);
+         rate = PerfMeasureRate(CreateUploadTexImage2D);
+         break;
+
+      case TEST_TEXIMAGE_MIPMAP:
+         g_initialwidth = g_width;
+         g_initialheight = g_height;
+         bytesPerImage = GenerateMipmapImages();
+         rate = PerfMeasureRate(UploadTexImage2DMipmap);
+         /* ?? each mipmap has a different bytes per image, is mb/s and mt/s
+            calculation below correct?  ?? */
+         break;
+
+      case TEST_TEXSUBIMAGE:
+         /* create initial, empty texture */
+         bytesPerImage = g_width * g_height * g_texelsize;
+         TexImage = malloc(bytesPerImage);
+         glTexImage2D(GL_TEXTURE_2D, 0, g_texintfmt,
+                      g_width, g_height, 0,
+                      g_texfmt, g_texsrctype, NULL);
+         rate = PerfMeasureRate(UploadTexSubImage2D);
+         break;
+
+      case TEST_GETTEXIMAGE:
+         bytesPerImage = g_width * g_height * g_texelsize;
+         TexImage = malloc(bytesPerImage);
+         glTexImage2D(GL_TEXTURE_2D, 0, g_texintfmt,
+                      g_width, g_height, 0,
+                      g_texfmt, g_texsrctype, TexImage);
+         rate = PerfMeasureRate(GetTexImage2D);
+         break;
+
+      default:
+         exit(1);
+      }
 
-   /* loop over source data formats */
-   for (fmt = 0; SrcFormats[fmt].format; fmt++) {
-      TexIntFormat = SrcFormats[fmt].internal_format;
-      TexSrcFormat = SrcFormats[fmt].format;
-      TexSrcType = SrcFormats[fmt].type;
-
-      /* loop over glTexImage, glTexSubImage */
-      for (mode = 0; mode < MODE_COUNT; mode++) {
-         GLuint minsz, maxsz;
-
-         if (SrcFormats[fmt].full_test) {
-            minsz = 16;
-            maxsz = 4096;
-         }
-         else {
-            minsz = maxsz = 256;
-            if (mode == MODE_CREATE_TEXIMAGE)
-               continue;
-         }
-
-         /* loop over a defined range of texture sizes, test only the
-          * ones which are legal for this driver.
-          */
-         for (TexSize = minsz; TexSize <= maxsz; TexSize *= 4) {
-            double mbPerSec;
-
-            if (TexSize <= maxSize) {
-               GLint bytesPerImage;
-
-               bytesPerImage = TexSize * TexSize * SrcFormats[fmt].texel_size;
-               TexImage = malloc(bytesPerImage);
-
-               switch (mode) {
-               case MODE_TEXIMAGE:
-                  rate = PerfMeasureRate(UploadTexImage2D);
-                  break;
-
-               case MODE_CREATE_TEXIMAGE:
-                  rate = PerfMeasureRate(CreateUploadTexImage2D);
-                  break;
-                  
-               case MODE_TEXSUBIMAGE:
-                  /* create initial, empty texture */
-                  glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
-                               TexSize, TexSize, 0,
-                               TexSrcFormat, TexSrcType, NULL);
-                  rate = PerfMeasureRate(UploadTexSubImage2D);
-                  break;
-
-               case MODE_GETTEXIMAGE:
-                  glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
-                               TexSize, TexSize, 0,
-                               TexSrcFormat, TexSrcType, TexImage);
-                  rate = PerfMeasureRate(GetTexImage2D);
-                  break;
-
-               default:
-                  exit(1);
-               }
-
-               mbPerSec = rate * bytesPerImage / (1024.0 * 1024.0);
-               free(TexImage);
-
-
-               {
-                  unsigned err;
-                  err = glGetError();
-                  if (err) {
-                     perf_printf("non-zero glGetError() %d\n", err);
-                     exit(1);
-                  }
-               }  
-
-            }
-            else {
-               rate = 0;
-               mbPerSec = 0;
-            }
+      mbPerSec = (rate * bytesPerImage) / (1024.0 * 1024.0);
+      mTPerSec = (rate * g_width * g_height) / (1024.0 * 1024.0);
+      free(TexImage);
 
-            perf_printf("  %s(%s %d x %d): "
-                        "%.1f images/sec, %.1f MB/sec\n",
-                        mode_name[mode],
-                        SrcFormats[fmt].name, TexSize, TexSize, rate, mbPerSec);
+      {
+         unsigned err;
+         err = glGetError();
+         if (err) {
+            perf_printf("non-zero glGetError() %d\n", err);
+            exit(1);
          }
+      }
 
-         if (SrcFormats[fmt].full_test) 
-            perf_printf("\n");
+      if (g_csvstyle == CSV_STYLE_DATA || g_csvstyle == CSV_STYLE_FULL) {
+         /* print a unique name to add in spreadsheet computations */
+         perf_printf("%s-%d-%d-%d-%s-%d-%s-%s, ",
+                     test_name[test], g_width, g_height, g_level,
+                     typeName, g_texelsize, formatName, internalformatName);
+         perf_printf("%s, %d, %d, %d, %s, %d, %s, %s, %.1f, %.1f, %.1f\n",
+                     test_name[test], g_width, g_height, g_level, typeName, g_texelsize, formatName,
+                     internalformatName, rate, mbPerSec, mTPerSec);
+      } else {
+         perf_printf("%s(%s %dx%d): " "%.1f images/sec, %.1f MB/sec\n",
+                     test_name[test],
+                     configName, g_width, g_height, rate, mbPerSec);
       }
    }
 

Comments

Looks good, just some minor clean-up suggestions below.

For patch 3, the idea of using loops sounds good.

Otherwise, Reviewed-by: Brian Paul <brianp@vmware.com>

-Brian

On 11/11/2013 05:53 PM, Courtney Goeltzenleuchter wrote:
> Needed test to measure texture upload speed under a variety
> of modes (mipmap, source format, internal format, size, etc.)
> This new test has an interactive run mode like the other Mesa
> Perf tests but also includes command line options to make
> it automatable.
> Fix up code formatting.
> Integrate review feedback.
>
> This provides a quick way to get feedback on texture upload
> related performance tuning.
> Texture image data is initialized and aligned to 64 byte bounary.
> Uses Mesa demos Perf library to do the measurements.
>
> Signed-off-by: Courtney Goeltzenleuchter <courtney@LunarG.com>
> ---
>   src/perf/teximage.c | 720 ++++++++++++++++++++++++++++++++++++++++------------
>   1 file changed, 563 insertions(+), 157 deletions(-)
>
> diff --git a/src/perf/teximage.c b/src/perf/teximage.c
> index 88316f3..3ab33a5 100644
> --- a/src/perf/teximage.c
> +++ b/src/perf/teximage.c
> @@ -1,5 +1,6 @@
>   /*
>    * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
> + * Copyright (C) 2012-2013 LunarG, Inc.
>    *
>    * Permission is hereby granted, free of charge, to any person obtaining a
>    * copy of this software and associated documentation files (the "Software"),
> @@ -14,49 +15,64 @@
>    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
>    * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> - * VMWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
> + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
>    * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
>    * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + *
>    */
>
>   /**
> - * Measure glTex[Sub]Image2D() and glGetTexImage() rate
> - *
> - * Brian Paul
> - * 16 Sep 2009
> + * Measure glTexImage2D() rate
> + *   enhanced from teximage.c by Brian Paul, 16 Sep 2009
> + *      2013 - Lisa Owens (lisa at lunarg.com)
> + *           - Courtney Goeltzenleuchter (courtney at lunarg.com)
>    */
>
>   #include "glmain.h"
>   #include "common.h"
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdbool.h>
>
> +/**
> + * Align a value up to an alignment value
> + *
> + * If \c value is not already aligned to the requested alignment value, it
> + * will be rounded up.
> + *
> + * \param value  Value to be rounded
> + * \param alignment  Alignment value to be used.  This must be a power of two.
> + *
> + * \sa ROUND_DOWN_TO()
> + */
> +#define ALIGN_PTR(value, alignment)  (((uintptr_t)(value) + (alignment) - 1) & ~ (uintptr_t)((alignment) - 1))
>
>   int WinWidth = 100, WinHeight = 100;
>
> +/* for texture creation */
>   static GLuint VBO;
>   static GLuint TexObj = 0;
>   static GLubyte *TexImage = NULL;
> -static GLsizei TexSize;
> -static GLenum TexIntFormat, TexSrcFormat, TexSrcType;
> -
> -static const GLboolean DrawPoint = GL_TRUE;
> -static const GLboolean TexSubImage4 = GL_FALSE;
>
>   enum {
> -   MODE_CREATE_TEXIMAGE,
> -   MODE_TEXIMAGE,
> -   MODE_TEXSUBIMAGE,
> -   MODE_GETTEXIMAGE,
> -   MODE_COUNT
> +   TEST_CREATE_TEXIMAGE,
> +   TEST_TEXIMAGE,
> +   TEST_TEXIMAGE_MIPMAP,
> +   TEST_TEXSUBIMAGE,
> +   TEST_GETTEXIMAGE,
> +   TEST_COUNT
>   };
>
> -static const char *mode_name[MODE_COUNT] =
> -{
> +static const char *test_name[TEST_COUNT] = {
>      "Create_TexImage",
>      "TexImage",
> +   "TexImage_Mipmap",
>      "TexSubImage",
>      "GetTexImage"
>   };
>
> +GLboolean test_enable[TEST_COUNT];
>
>
>   struct vertex
> @@ -68,8 +84,322 @@ static const struct vertex vertices[1] = {
>      { 0.0, 0.0, 0.5, 0.5 },
>   };
>
> +
> +/** define some functions */
> +GLenum parseType(char *);
> +GLenum parseFormat(char *, int);
> +GLuint determineTexelSize(GLenum format, GLenum type);
> +void parseConfigFile(void);
> +GLuint GenerateMipmapImages(void);

I think these declarations could be removed it you just declare the 
functions static below.


> +
>   #define VOFFSET(F) ((void *) offsetof(struct vertex, F))
>
> +/** defaults; options can be provided to set these */
> +GLint g_level = 0;
> +GLsizei g_width = 256;
> +GLsizei g_height = 256;
> +GLuint g_texelsize = 4;
> +GLenum g_texsrctype = GL_UNSIGNED_BYTE;
> +GLenum g_texfmt = GL_RGBA;
> +GLenum g_texintfmt = 0;
> +GLboolean g_drawpoint = GL_TRUE;
> +GLboolean g_subtexquad = GL_FALSE;
> +char configName[2056];
> +int formattype = 1;
> +int intformattype = 2;
> +char typeName[256] = "GL_UNSIGNED_BYTE";
> +char formatName[256] = "GL_RGBA";
> +char internalformatName[256] = "GL_RGBA";

The g_ prefix indicates a global variable.  It might be nice if we were 
more consistent about that.


> +GLboolean g_verbose = GL_FALSE;
> +
> +/* used when mipmapping */
> +static GLsizei g_initialwidth;
> +static GLsizei g_initialheight;
> +int g_numLevel = 0;
> +GLubyte *g_mipmapimgs[64];
> +GLsizei g_mmwidths[64];
> +GLsizei g_mmheights[64];
> +
> +enum csvStyle {
> +   CSV_STYLE_OFF,
> +   CSV_STYLE_DATA,
> +   CSV_STYLE_FULL
> +};
> +enum csvStyle g_csvstyle = CSV_STYLE_OFF;
> +
> +struct gl_parse_type {
> +   char *name;
> +   GLenum type;
> +   bool fixed_size; /* true indicates texel size is component size */
> +   int component_size;
> +};
> +
> +struct gl_parse_type parse_type_table[] = {

Should the table be declared const?


> +   {"GL_UNSIGNED_BYTE", GL_UNSIGNED_BYTE, false, 1},
> +   {"GL_BYTE", GL_BYTE, false, 1},
> +   {"GL_UNSIGNED_SHORT", GL_UNSIGNED_SHORT, false, 2},
> +   {"GL_SHORT", GL_SHORT, false, 2},
> +   {"GL_UNSIGNED_INT", GL_UNSIGNED_INT, false, 4},
> +   {"GL_INT", GL_INT, false, 4},
> +   {"GL_FLOAT", GL_FLOAT, false, 4},
> +   {"GL_UNSIGNED_BYTE_3_3_2", GL_UNSIGNED_BYTE_3_3_2, true, 1},
> +   {"GL_UNSIGNED_BYTE_2_3_3_REV", GL_UNSIGNED_BYTE_2_3_3_REV, true, 1},
> +   {"GL_UNSIGNED_SHORT_5_6_5", GL_UNSIGNED_SHORT_5_6_5, true, 2},
> +   {"GL_UNSIGNED_SHORT_5_6_5_REV", GL_UNSIGNED_SHORT_5_6_5_REV, true, 2},
> +   {"GL_UNSIGNED_SHORT_4_4_4_4", GL_UNSIGNED_SHORT_4_4_4_4, true, 2},
> +   {"GL_UNSIGNED_SHORT_4_4_4_4_REV", GL_UNSIGNED_SHORT_4_4_4_4_REV, true, 2},
> +   {"GL_UNSIGNED_SHORT_5_5_5_1", GL_UNSIGNED_SHORT_5_5_5_1, true, 2},
> +   {"GL_UNSIGNED_SHORT_1_5_5_5_REV", GL_UNSIGNED_SHORT_1_5_5_5_REV, true, 2},
> +   {"GL_UNSIGNED_INT_8_8_8_8", GL_UNSIGNED_INT_8_8_8_8, true, 4},
> +   {"GL_UNSIGNED_INT_8_8_8_8_REV", GL_UNSIGNED_INT_8_8_8_8_REV, true, 4},
> +   {"GL_UNSIGNED_INT_10_10_10_2", GL_UNSIGNED_INT_10_10_10_2, true, 4},
> +   {"GL_UNSIGNED_INT_2_10_10_10_REV", GL_UNSIGNED_INT_2_10_10_10_REV, true, 4},
> +};
> +
> +/** parse the type string */
> +GLenum
> +parseType(char *typestr)
> +{
> +   GLenum ogltype;
> +   int i;
> +
> +   for (i=0; i<(sizeof(parse_type_table)/sizeof(struct gl_parse_type)); i++) {

You might want to defined a ARRAY_SIZE() macro to use there.


> +      if (strcmp(typestr, parse_type_table[i].name) == 0) {
> +         strcpy(typeName, typestr);
> +         return parse_type_table[i].type;
> +      }
> +   }
> +
> +   /* if we get here, type specified is invalid; use a default */
> +   perf_printf("\nwarning: type %s unknown; using GL_UNSIGNED_BYTE\n", typestr);
> +   ogltype = GL_UNSIGNED_BYTE;
> +   strcpy(typeName, "GL_UNSIGNED_BYTE");
> +
> +   return ogltype;
> +}
> +
> +/** parse the format string */
> +GLenum
> +parseFormat(char *formatstr, int ftype)
> +{
> +   GLenum oglformat;
> +
> +   if (ftype == formattype) {
> +      strcpy(formatName, formatstr);
> +   } else {
> +      strcpy(internalformatName, formatstr);
> +   }
> +
> +   if (strcmp(formatstr, "GL_RGB") == 0) {
> +      oglformat = GL_RGB;
> +   } else if (strcmp(formatstr, "GL_RGBA") == 0) {
> +      oglformat = GL_RGBA;
> +   } else if (strcmp(formatstr, "GL_BGRA") == 0) {
> +      oglformat = GL_BGRA;
> +   } else if (strcmp(formatstr, "GL_LUMINANCE") == 0) {
> +      oglformat = GL_LUMINANCE;
> +   } else {
> +      /* if we get here, format specified is invalid; use a default */
> +      perf_printf("\nwarning: format %s unknown; using GL_RGBA\n", formatstr);
> +      oglformat = GL_RGB;
> +      if (ftype == formattype) {
> +         strcpy(formatName, "GL_RGBA");
> +      } else {
> +         strcpy(internalformatName, "GL_RGBA");
> +      }
> +   }
> +
> +   return oglformat;
> +}
> +
> +GLuint
> +determineTexelSize(GLenum format, GLenum type)
> +{
> +   int i, component_size = 1, num_components = 1;
> +
> +   for (i=0; i<(sizeof(parse_type_table)/sizeof(struct gl_parse_type)); i++) {
> +      if (type == parse_type_table[i].type) {
> +         if (parse_type_table[i].fixed_size)
> +            return parse_type_table[i].component_size;
> +
> +         component_size = parse_type_table[i].component_size;
> +         break;
> +      }
> +   }
> +
> +   switch (format)
> +   {
> +   case GL_RGBA:
> +   case GL_BGRA:
> +      num_components = 4;
> +      break;
> +
> +   case GL_RGB:
> +      num_components = 3;
> +      break;
> +
> +   case GL_LUMINANCE:
> +      num_components = 1;
> +      break;
> +
> +   default:
> +      num_components = 4;
> +   }
> +
> +   return component_size * num_components;
> +}
> +
> +/** parse the args in the teximage_enh.opts file */
> +static void
> +parseCommands(int argc, char *argv[])
> +{
> +   char *key, *value;
> +   int intval;
> +   int arg, i;
> +
> +   bzero(test_enable, sizeof (test_enable));
> +
> +   for (arg = 1; arg < argc; arg++) {
> +      /* parse the arguments and set options appropriately */
> +      key = argv[arg];
> +      if (arg < argc) {
> +         /* point at next arg as most options require one. */
> +         value = argv[arg + 1];
> +      } else {
> +         value = "0"; /* default arg */
> +      }
> +      if (strcmp(key, "--width") == 0) {
> +         arg++;
> +         if (arg < argc) {
> +            intval = atoi(value);
> +            if (intval != 0) {
> +               g_width = (GLsizei) intval;
> +            }
> +         }
> +      } else if (strcmp(key, "--height") == 0) {
> +         if (arg < argc) {
> +            arg++;
> +            intval = atoi(value);
> +            if (intval != 0) {
> +               g_height = (GLsizei) intval;
> +            }
> +         }
> +      } else if (strcmp(key, "--level") == 0) {
> +         arg++;
> +         if (arg < argc) {
> +            intval = atoi(value);
> +            g_level = (GLint) intval;
> +         }
> +      } else if (strcmp(key, "--drawpoint") == 0) {
> +         /* default is true, so check for false */
> +         if (arg < argc) {
> +            arg++;
> +            if (strcmp(value, "no") == 0) {
> +               g_drawpoint = GL_FALSE;
> +            } else if (strcmp(value, "yes") == 0) {
> +               g_drawpoint = GL_TRUE;
> +            }
> +         }
> +      } else if (strcmp(key, "--type") == 0) {
> +         if (arg < argc) {
> +            arg++;
> +            g_texsrctype = parseType(value);
> +         }
> +      } else if (strcmp(key, "--format") == 0) {
> +         if (arg < argc) {
> +            arg++;
> +            g_texfmt = parseFormat(value, formattype);
> +         }
> +      } else if (strcmp(key, "--internalformat") == 0) {
> +         if (arg < argc) {
> +            arg++;
> +            g_texintfmt = parseFormat(value, intformattype);
> +         }
> +      } else if (strcmp(key, "--test") == 0) {
> +         if (arg < argc) {
> +            arg++;
> +            for (i = 0; i < TEST_COUNT; i++) {
> +               if (strcmp(value, test_name[i]) == 0) {
> +                  test_enable[i] = GL_TRUE;
> +                  break;
> +               }
> +            }
> +            if (i == TEST_COUNT) {
> +               perf_printf("Invalid test specified: %s", value);
> +               exit(1);
> +            }
> +         }
> +      } else if (strcmp(key, "--verbose") == 0) {
> +         g_verbose = GL_TRUE;
> +      } else if (strcmp(key, "--csvstyle") == 0) {
> +         if (arg < argc) {
> +            arg++;
> +            if (strcmp(value, "off") == 0) {
> +               g_csvstyle = CSV_STYLE_OFF; /* Show standard output */
> +            } else if (strcmp(value, "data") == 0) {
> +               g_csvstyle = CSV_STYLE_DATA; /* Show only perf stats */
> +            } else if (strcmp(value, "full") == 0) {
> +               g_csvstyle = CSV_STYLE_FULL; /* show header and perf stats */
> +            }
> +         }
> +      } else if (strcmp(key, "--help") == 0) {
> +         perf_printf("run glTexImage2D benchmark\n");
> +         perf_printf("Usage: %s [--width <num>] [--height <num>] [--level <num>]\n", argv[0]);
> +         perf_printf("                [--type <glenum>] [--texelsize <num>] [--format <glenum>]\n");
> +         perf_printf("                [--internalformat <gltype>] [--drawpoint yes/no] [--verbose]\n");
> +         perf_printf("                [--csvstyle off|data|full] [--test ");
> +         for (i = 0; i < TEST_COUNT; i++) {
> +            perf_printf("%s", test_name[i]);
> +            /* print separator all but last one */
> +            if (i < (TEST_COUNT - 1)) perf_printf(" | ");

perf_print() call on next line.


> +         }
> +         perf_printf("]\n");
> +         exit(0);
> +      } else {
> +         perf_printf("Unrecognized argument: %s\n", key);
> +         exit(1);
> +      }
> +   }
> +
> +   g_texelsize = determineTexelSize(g_texfmt, g_texsrctype);
> +
> +   for (i = 0; i < TEST_COUNT; i++) {
> +      if (test_enable[i] == GL_TRUE)
> +         break;
> +   }
> +   if (i == TEST_COUNT) {
> +      /* No tests were enabled, so enable all of them */
> +      for (i = 0; i < TEST_COUNT; i++) {
> +         test_enable[i] = GL_TRUE;
> +      }
> +   }
> +
> +   /* if internal format not provided, set to source format */
> +   if (g_texintfmt == 0) {
> +      g_texintfmt = g_texfmt;
> +   }
> +
> +   /* generate config name for output */
> +   sprintf(configName, "%s/%s/%s", formatName, internalformatName, typeName);
> +   if (g_csvstyle == CSV_STYLE_OFF && g_verbose)
> +      perf_printf("config name (fmt/intfmt/type): %s\n", configName);
> +
> +   /* print out values using for this test run */
> +   if (g_verbose) {
> +      perf_printf("running benchmark with the following options:\n");
> +      perf_printf("   type: %s:%d\n", typeName, g_texsrctype);
> +      perf_printf("   format: %s:%d\n", formatName, g_texfmt);
> +      perf_printf("   internalformat: %s:%d\n", internalformatName, g_texintfmt);
> +      perf_printf("   level: %d\n", g_level);
> +      perf_printf("   width: %d\n", g_width);
> +      perf_printf("   height: %d\n", g_height);
> +      perf_printf("   texel size: %d\n", g_texelsize);
> +      perf_printf("   draw point?: %d\n", g_drawpoint);
> +   }
> +
> +   return;
> +
> +}
>
>   /** Called from test harness/main */
>   void
> @@ -91,15 +421,20 @@ PerfInit(int argc, char *argv[])
>      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
>      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
>      glEnable(GL_TEXTURE_2D);
> -}
> -
> -
>
> +   /* parse config file */
> +   parseCommands(argc, argv);
> +}
>
>   static void
>   CreateUploadTexImage2D(unsigned count)
>   {
>      unsigned i;
> +
> +   if (g_verbose) {
> +      perf_printf("CreateUploadTexImage2D count: %d\n", count);
> +   }
> +
>      for (i = 0; i < count; i++) {
>         if (TexObj)
>            glDeleteTextures(1, &TexObj);
> @@ -109,68 +444,135 @@ CreateUploadTexImage2D(unsigned count)
>         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
>         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
>
> -      glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
> -                   TexSize, TexSize, 0,
> -                   TexSrcFormat, TexSrcType, TexImage);
> +      glTexImage2D(GL_TEXTURE_2D, g_level, g_texintfmt,
> +                   g_width, g_height, 0,
> +                   g_texfmt, g_texsrctype, TexImage);
>
> -      if (DrawPoint)
> +      if (g_drawpoint)
>            glDrawArrays(GL_POINTS, 0, 1);
>      }
>      glFinish();
>   }
>
> -
>   static void
>   UploadTexImage2D(unsigned count)
>   {
>      unsigned i;
> +
> +   if (g_verbose) {
> +      perf_printf("UploadTexImage2D count: %d\n", count);
> +   }
> +
>      for (i = 0; i < count; i++) {
> -      /* XXX is this equivalent to a glTexSubImage call since we're
> -       * always specifying the same image size?  That case isn't optimized
> -       * in Mesa but may be optimized in other drivers.  Note sure how
> -       * much difference that might make.
> -       */
> -      glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
> -                   TexSize, TexSize, 0,
> -                   TexSrcFormat, TexSrcType, TexImage);
> -      if (DrawPoint)
> +      glTexImage2D(GL_TEXTURE_2D, g_level, g_texintfmt,
> +                   g_width, g_height, 0,
> +                   g_texfmt, g_texsrctype, TexImage);
> +      if (g_drawpoint)
>            glDrawArrays(GL_POINTS, 0, 1);
>      }
>      glFinish();
>   }
>
> +GLuint
> +GenerateMipmapImages(void)
> +{
> +   GLuint w, h;
> +   GLint bytesPerImage, mallocSize, bytesPerLevel;
> +   GLubyte *levelPtr;
> +   w = g_width;
> +   h = g_height;
> +   bytesPerImage = 0;
> +   mallocSize = 0;
> +
> +   /* compute totals first */
> +   while ((w > 1) || (h > 1)) {
> +      bytesPerLevel = w * h * g_texelsize;
> +      bytesPerImage += bytesPerLevel;
> +      /* Add 64 so that we can align the data to 64 byte boundary */
> +      mallocSize += bytesPerLevel + 0x40;
> +
> +      if (w > 1) {
> +         w = w / 2;
> +      }
> +      if (h > 1) {
> +         h = h / 2;
> +      }
> +   }
> +
> +   TexImage = malloc(mallocSize);
> +   levelPtr = TexImage;
> +
> +   w = g_width;
> +   h = g_height;
> +   /* generate the mipmap images */
> +   while ((w > 1) || (h > 1)) {
> +      bytesPerLevel = w * h * g_texelsize;
> +
> +      /* Round to the next higher 64byte boundary.
> +       * That is generally better for today's HW and
> +       * easy for an app to do if they care.
> +       */
> +      levelPtr = (GLubyte *) ALIGN_PTR(levelPtr, 64);
> +
> +      /* set memory to "simulate" loading an image */
> +      memset(levelPtr, g_numLevel, bytesPerLevel);
> +
> +      g_mipmapimgs[g_numLevel] = levelPtr;
> +      g_mmwidths[g_numLevel] = w;
> +      g_mmheights[g_numLevel] = h;
> +      g_numLevel = g_numLevel + 1;
> +
> +      /* Advance pointer to next free area */
> +      levelPtr += bytesPerLevel;
> +
> +      if (w > 1) {
> +         w = w / 2;
> +      }
> +      if (h > 1) {
> +         h = h / 2;
> +      }
> +   }
> +   if (g_verbose) {
> +      perf_printf("GenerateMipmapImages: generated %d mipmaps\n", g_numLevel);
> +   }
> +
> +   return bytesPerImage;
> +
> +}
>
>   static void
>   UploadTexSubImage2D(unsigned count)
>   {
>      unsigned i;
>      for (i = 0; i < count; i++) {
> -      if (TexSubImage4) {
> -         GLsizei halfSize = (TexSize == 1) ? 1 : TexSize / 2;
> -         GLsizei halfPos = TexSize - halfSize;
> +      if (g_subtexquad) {
> +         GLsizei halfSizeW = (g_width == 1) ? 1 : g_width / 2;
> +         GLsizei halfPosX = g_width - halfSizeW;
> +         GLsizei halfSizeH = (g_height == 1) ? 1 : g_height / 2;
> +         GLsizei halfPosY = g_height - halfSizeH;
>            /* do glTexSubImage2D in four pieces */
>            /* lower-left */
> -         glPixelStorei(GL_UNPACK_ROW_LENGTH, TexSize);
> +         glPixelStorei(GL_UNPACK_ROW_LENGTH, g_width);
>            glTexSubImage2D(GL_TEXTURE_2D, 0,
> -                         0, 0, halfSize, halfSize,
> -                         TexSrcFormat, TexSrcType, TexImage);
> +                         0, 0, halfSizeW, halfSizeH,
> +                         g_texfmt, g_texsrctype, TexImage);
>            /* lower-right */
> -         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPos);
> +         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPosX);
>            glTexSubImage2D(GL_TEXTURE_2D, 0,
> -                         halfPos, 0, halfSize, halfSize,
> -                         TexSrcFormat, TexSrcType, TexImage);
> +                         halfPosX, 0, halfSizeW, halfSizeH,
> +                         g_texfmt, g_texsrctype, TexImage);
>            /* upper-left */
>            glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
> -         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPos);
> +         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPosY);
>            glTexSubImage2D(GL_TEXTURE_2D, 0,
> -                         0, halfPos, halfSize, halfSize,
> -                         TexSrcFormat, TexSrcType, TexImage);
> +                         0, halfPosY, halfSizeW, halfSizeH,
> +                         g_texfmt, g_texsrctype, TexImage);
>            /* upper-right */
> -         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPos);
> -         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPos);
> +         glPixelStorei(GL_UNPACK_SKIP_PIXELS, halfPosX);
> +         glPixelStorei(GL_UNPACK_SKIP_ROWS, halfPosY);
>            glTexSubImage2D(GL_TEXTURE_2D, 0,
> -                         halfPos, halfPos, halfSize, halfSize,
> -                         TexSrcFormat, TexSrcType, TexImage);
> +                         halfPosX, halfPosY, halfSizeW, halfSizeH,
> +                         g_texfmt, g_texsrctype, TexImage);
>            /* reset the unpacking state */
>            glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
>            glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
> @@ -179,46 +581,52 @@ UploadTexSubImage2D(unsigned count)
>         else {
>            /* replace whole texture image at once */
>            glTexSubImage2D(GL_TEXTURE_2D, 0,
> -                         0, 0, TexSize, TexSize,
> -                         TexSrcFormat, TexSrcType, TexImage);
> +                         0, 0, g_width, g_height,
> +                         g_texfmt, g_texsrctype, TexImage);
>         }
> -      if (DrawPoint)
> +      if (g_drawpoint)
>            glDrawArrays(GL_POINTS, 0, 1);
>      }
>      glFinish();
>   }
>
> -
>   static void
>   GetTexImage2D(unsigned count)
>   {
>      unsigned i;
> -   GLubyte *buf = (GLubyte *) malloc(TexSize * TexSize * 4);
> +   GLubyte *buf = (GLubyte *) malloc(g_width * g_height * g_texelsize);
>      for (i = 0; i < count; i++) {
>         glGetTexImage(GL_TEXTURE_2D, 0,
> -                    TexSrcFormat, TexSrcType, buf);
> +                    g_texfmt, g_texsrctype, buf);
>      }
>      glFinish();
>      free(buf);
>   }
>
> +static void
> +UploadTexImage2DMipmap(unsigned count)
> +{
> +   unsigned i, j;
>
> -/* XXX any other formats to measure? */
> -static const struct {
> -   GLenum format, type;
> -   GLenum internal_format;
> -   const char *name;
> -   GLuint texel_size;
> -   GLboolean full_test;
> -} SrcFormats[] = {
> -   { GL_RGBA, GL_UNSIGNED_BYTE,       GL_RGBA, "RGBA/ubyte", 4,   GL_TRUE },
> -   { GL_RGB, GL_UNSIGNED_BYTE,        GL_RGB, "RGB/ubyte", 3,     GL_FALSE },
> -   { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_RGB, "RGB/565", 2,       GL_FALSE },
> -   { GL_BGRA, GL_UNSIGNED_BYTE,       GL_RGBA, "BGRA/ubyte", 4,   GL_FALSE },
> -   { GL_LUMINANCE, GL_UNSIGNED_BYTE,  GL_LUMINANCE, "L/ubyte", 1, GL_FALSE },
> -   { 0, 0, 0, NULL, 0, 0 }
> -};
>
> +   /* ?? make this configurable ?? */
> +   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
> +   /* mipmaps previously generated, call glTexImage2D for all levels */
> +   for (i = 0; i < count; i++) {
> +      for (j = 0; j < g_numLevel; j++) {
> +         if (g_verbose) {
> +            perf_printf("mipmap glTexImage2D - level: %d, width: %d, height: %d\n",
> +                        j, g_mmwidths[j], g_mmheights[j]);
> +         }
> +         glTexImage2D(GL_TEXTURE_2D, j, g_texintfmt, g_mmwidths[j],
> +                      g_mmheights[j], 0, g_texfmt, g_texsrctype, g_mipmapimgs[j]);
> +         if (g_drawpoint)
> +            glDrawArrays(GL_POINTS, 0, 1);
> +      }
> +   }
> +   glFinish();
> +
> +}
>
>   /** Called from test harness/main */
>   void
> @@ -226,104 +634,102 @@ PerfNextRound(void)
>   {
>   }
>
> -
>   /** Called from test harness/main */
>   void
>   PerfDraw(void)
>   {
>      GLint maxSize;
>      double rate;
> -   GLint fmt, mode;
> +   GLint test;
> +   double mbPerSec; // Megabytes per second
> +   double mTPerSec; // MegaTexels per second
> +   GLint bytesPerImage;
> +
> +   if (g_csvstyle == CSV_STYLE_FULL) {
> +      perf_printf("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n",
> +                  "ID", "Test", "width", "height", "level", "type",
> +                  "texelsize", "format", "internalformat",
> +                  "images/sec", "MB/sec", "MT/sec");
> +   }
>
>      glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
> +   /* TODO - check for max size exceeded?? */
> +
> +   /* loop over glTexImage */
> +   for (test = 0; test < TEST_COUNT; test++) {
> +
> +      if (test_enable[test] == GL_FALSE) continue;
> +
> +      switch (test) {
> +      case TEST_TEXIMAGE:
> +         bytesPerImage = g_width * g_height * g_texelsize;
> +         TexImage = malloc(bytesPerImage);
> +         rate = PerfMeasureRate(UploadTexImage2D);
> +         break;
> +
> +      case TEST_CREATE_TEXIMAGE:
> +         bytesPerImage = g_width * g_height * g_texelsize;
> +         TexImage = malloc(bytesPerImage);
> +         rate = PerfMeasureRate(CreateUploadTexImage2D);
> +         break;
> +
> +      case TEST_TEXIMAGE_MIPMAP:
> +         g_initialwidth = g_width;
> +         g_initialheight = g_height;
> +         bytesPerImage = GenerateMipmapImages();
> +         rate = PerfMeasureRate(UploadTexImage2DMipmap);
> +         /* ?? each mipmap has a different bytes per image, is mb/s and mt/s
> +            calculation below correct?  ?? */
> +         break;
> +
> +      case TEST_TEXSUBIMAGE:
> +         /* create initial, empty texture */
> +         bytesPerImage = g_width * g_height * g_texelsize;
> +         TexImage = malloc(bytesPerImage);
> +         glTexImage2D(GL_TEXTURE_2D, 0, g_texintfmt,
> +                      g_width, g_height, 0,
> +                      g_texfmt, g_texsrctype, NULL);
> +         rate = PerfMeasureRate(UploadTexSubImage2D);
> +         break;
> +
> +      case TEST_GETTEXIMAGE:
> +         bytesPerImage = g_width * g_height * g_texelsize;
> +         TexImage = malloc(bytesPerImage);
> +         glTexImage2D(GL_TEXTURE_2D, 0, g_texintfmt,
> +                      g_width, g_height, 0,
> +                      g_texfmt, g_texsrctype, TexImage);
> +         rate = PerfMeasureRate(GetTexImage2D);
> +         break;
> +
> +      default:
> +         exit(1);
> +      }
>
> -   /* loop over source data formats */
> -   for (fmt = 0; SrcFormats[fmt].format; fmt++) {
> -      TexIntFormat = SrcFormats[fmt].internal_format;
> -      TexSrcFormat = SrcFormats[fmt].format;
> -      TexSrcType = SrcFormats[fmt].type;
> -
> -      /* loop over glTexImage, glTexSubImage */
> -      for (mode = 0; mode < MODE_COUNT; mode++) {
> -         GLuint minsz, maxsz;
> -
> -         if (SrcFormats[fmt].full_test) {
> -            minsz = 16;
> -            maxsz = 4096;
> -         }
> -         else {
> -            minsz = maxsz = 256;
> -            if (mode == MODE_CREATE_TEXIMAGE)
> -               continue;
> -         }
> -
> -         /* loop over a defined range of texture sizes, test only the
> -          * ones which are legal for this driver.
> -          */
> -         for (TexSize = minsz; TexSize <= maxsz; TexSize *= 4) {
> -            double mbPerSec;
> -
> -            if (TexSize <= maxSize) {
> -               GLint bytesPerImage;
> -
> -               bytesPerImage = TexSize * TexSize * SrcFormats[fmt].texel_size;
> -               TexImage = malloc(bytesPerImage);
> -
> -               switch (mode) {
> -               case MODE_TEXIMAGE:
> -                  rate = PerfMeasureRate(UploadTexImage2D);
> -                  break;
> -
> -               case MODE_CREATE_TEXIMAGE:
> -                  rate = PerfMeasureRate(CreateUploadTexImage2D);
> -                  break;
> -
> -               case MODE_TEXSUBIMAGE:
> -                  /* create initial, empty texture */
> -                  glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
> -                               TexSize, TexSize, 0,
> -                               TexSrcFormat, TexSrcType, NULL);
> -                  rate = PerfMeasureRate(UploadTexSubImage2D);
> -                  break;
> -
> -               case MODE_GETTEXIMAGE:
> -                  glTexImage2D(GL_TEXTURE_2D, 0, TexIntFormat,
> -                               TexSize, TexSize, 0,
> -                               TexSrcFormat, TexSrcType, TexImage);
> -                  rate = PerfMeasureRate(GetTexImage2D);
> -                  break;
> -
> -               default:
> -                  exit(1);
> -               }
> -
> -               mbPerSec = rate * bytesPerImage / (1024.0 * 1024.0);
> -               free(TexImage);
> -
> -
> -               {
> -                  unsigned err;
> -                  err = glGetError();
> -                  if (err) {
> -                     perf_printf("non-zero glGetError() %d\n", err);
> -                     exit(1);
> -                  }
> -               }
> -
> -            }
> -            else {
> -               rate = 0;
> -               mbPerSec = 0;
> -            }
> +      mbPerSec = (rate * bytesPerImage) / (1024.0 * 1024.0);
> +      mTPerSec = (rate * g_width * g_height) / (1024.0 * 1024.0);
> +      free(TexImage);
>
> -            perf_printf("  %s(%s %d x %d): "
> -                        "%.1f images/sec, %.1f MB/sec\n",
> -                        mode_name[mode],
> -                        SrcFormats[fmt].name, TexSize, TexSize, rate, mbPerSec);
> +      {
> +         unsigned err;
> +         err = glGetError();
> +         if (err) {
> +            perf_printf("non-zero glGetError() %d\n", err);
> +            exit(1);
>            }
> +      }
>
> -         if (SrcFormats[fmt].full_test)
> -            perf_printf("\n");
> +      if (g_csvstyle == CSV_STYLE_DATA || g_csvstyle == CSV_STYLE_FULL) {
> +         /* print a unique name to add in spreadsheet computations */
> +         perf_printf("%s-%d-%d-%d-%s-%d-%s-%s, ",
> +                     test_name[test], g_width, g_height, g_level,
> +                     typeName, g_texelsize, formatName, internalformatName);
> +         perf_printf("%s, %d, %d, %d, %s, %d, %s, %s, %.1f, %.1f, %.1f\n",
> +                     test_name[test], g_width, g_height, g_level, typeName, g_texelsize, formatName,
> +                     internalformatName, rate, mbPerSec, mTPerSec);
> +      } else {
> +         perf_printf("%s(%s %dx%d): " "%.1f images/sec, %.1f MB/sec\n",
> +                     test_name[test],
> +                     configName, g_width, g_height, rate, mbPerSec);
>         }
>      }
>
>