[Mesa-dev] [PATCH demos 3/3] Perf: teximage_enh Add command line options

Courtney Goeltzenleuchter courtney at lunarg.com
Thu Nov 7 13:16:19 PST 2013


texture_enh allows the user to specify source, internal formats
and mipmap or not. 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 at LunarG.com>
---
 src/perf/teximage_enh.c | 366 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 287 insertions(+), 79 deletions(-)

diff --git a/src/perf/teximage_enh.c b/src/perf/teximage_enh.c
index 9bb3944..0f5d2f7 100644
--- a/src/perf/teximage_enh.c
+++ b/src/perf/teximage_enh.c
@@ -31,6 +31,19 @@
 #include <stdio.h>
 #include <string.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 */
@@ -39,16 +52,20 @@ static GLuint TexObj = 0;
 static GLubyte *TexImage = NULL;
 
 enum {
-   MODE_CREATE_TEXIMAGE,
-   MODE_TEXIMAGE,
-   MODE_COUNT
+   TEST_CREATE_TEXIMAGE,
+   TEST_TEXIMAGE,
+   TEST_TEXIMAGE_MIPMAP,
+   TEST_COUNT
 };
 
-static const char *mode_name[MODE_COUNT] ={
+static const char *test_name[TEST_COUNT] = {
    "Create_TexImage",
    "TexImage",
+   "TexImage_Mipmap",
 };
 
+GLboolean test_enable[TEST_COUNT];
+
 struct vertex {
    GLfloat x, y, s, t;
 };
@@ -62,17 +79,17 @@ static const struct vertex vertices[1] = {
 GLenum parseType(char *);
 GLenum parseFormat(char *, int);
 void parseConfigFile(void);
+GLuint GenerateMipmapImages(void);
 
 #define VOFFSET(F) ((void *) offsetof(struct vertex, F))
 
-/** defaults; config file options can be provided to set these */
-static const char *configFile = "teximage_enh.opts";
-GLint g_level = 1;
+/** 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_RGB;
+GLenum g_texfmt = GL_RGBA;
 GLenum g_texintfmt = 0;
 GLboolean g_drawpoint = GL_TRUE;
 char configName[2056];
@@ -82,7 +99,21 @@ char typeName[256] = "GL_UNSIGNED_BYTE";
 char formatName[256] = "GL_RGB";
 char internalformatName[256] = "GL_RGB";
 GLboolean g_verbose = GL_FALSE;
-GLboolean g_csvstyle = 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;
 
 /** parse the type string */
 GLenum
@@ -141,12 +172,12 @@ parseFormat(char *formatstr, int ftype)
    } else {
       /* if we get here, format specified is invalid; use a default */
       perf_printf("\n");
-      perf_printf("warning: format %s unknown; using GL_RGB\n", formatstr);
+      perf_printf("warning: format %s unknown; using GL_RGBA\n", formatstr);
       oglformat = GL_RGB;
       if (ftype == formattype) {
-         strcpy(formatName, "GL_RGB");
+         strcpy(formatName, "GL_RGBA");
       } else {
-         strcpy(internalformatName, "GL_RGB");
+         strcpy(internalformatName, "GL_RGBA");
       }
    }
 
@@ -154,74 +185,135 @@ parseFormat(char *formatstr, int ftype)
 }
 
 /** parse the args in the teximage_enh.opts file */
-void
-parseConfigFile(void)
+static void
+parseCommands(int argc, char *argv[])
 {
-   FILE *fp;
    char *key, *value;
-   char *equal = "=";
-   char line[ 128];
-   int len;
    int intval;
+   int arg, i;
 
-   //perf_printf("parsing configuration file %s\n", configFile);
-   fp = fopen(configFile, "r");
-   if (!fp) {
-      perf_printf("%s file does not exist, using defaults\n", configFile);
-   } else {
-      while (fgets(line, sizeof line, fp) != NULL) {
-         //perf_printf("config file line read: %s\n", line);
-         /* TODO - better check for blank line */
-         if ((strncmp(line, "#", 1) == 0) || (line[0] == '\n')) {
-            /* comment or blank line, continue */
-            //perf_printf("found comment or blank line; continuing\n");
-            continue;
-         }
+   bzero(test_enable, sizeof (test_enable));
 
-         /* get the key and value */
-         key = strtok(line, equal);
-         value = strtok(NULL, equal);
-         intval = atoi(value);
-
-         /* remove line feed */
-         len = strlen(value);
-         if (value[len - 1] == '\n') {
-            value[len - 1] = 0;
-         }
-
-         /* parse the arguments and set options appropriately */
-         if (strcmp(key, "width") == 0) {
+   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) {
+         }
+      } 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) {
+         }
+      } 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 (strcmp(value, "false") == 0) {
+         }
+      } 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, "texelsize") == 0) {
+         }
+      } else if (strcmp(key, "--texelsize") == 0) {
+         if (arg < argc) {
+            arg++;
+            intval = atoi(value);
             if (intval != 0) {
                g_texelsize = (GLuint) intval;
             }
-         } else if (strcmp(key, "type") == 0) {
+         }
+      } else if (strcmp(key, "--type") == 0) {
+         if (arg < argc) {
+            arg++;
             g_texsrctype = parseType(value);
-         } else if (strcmp(key, "format") == 0) {
+         }
+      } else if (strcmp(key, "--format") == 0) {
+         if (arg < argc) {
+            arg++;
             g_texfmt = parseFormat(value, formattype);
-         } else if (strcmp(key, "internalformat") == 0) {
+         }
+      } else if (strcmp(key, "--internalformat") == 0) {
+         if (arg < argc) {
+            arg++;
             g_texintfmt = parseFormat(value, intformattype);
-         } else if (strcmp(key, "verbose") == 0) {
-            g_verbose = GL_TRUE;
-         } else if (strcmp(key, "csvstyle") == 0) {
-            g_csvstyle = GL_TRUE;
          }
+      } 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);
       }
-      fclose(fp);
+   }
+
+   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 */
@@ -231,7 +323,8 @@ parseConfigFile(void)
 
    /* generate config name for output */
    sprintf(configName, "%s/%s/%s", formatName, internalformatName, typeName);
-   perf_printf("config name (fmt/intfmt/type): %s\n", configName);
+   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) {
@@ -252,7 +345,7 @@ parseConfigFile(void)
 
 /** Called from test harness/main */
 void
-PerfInit(void)
+PerfInit(int argc, char *argv[])
 {
    /* setup VBO w/ vertex data */
    glGenBuffersARB(1, &VBO);
@@ -272,7 +365,7 @@ PerfInit(void)
    glEnable(GL_TEXTURE_2D);
 
    /* parse config file */
-   parseConfigFile();
+   parseCommands(argc, argv);
 }
 
 static void
@@ -322,6 +415,99 @@ UploadTexImage2D(unsigned count)
    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
+UploadTexImage2DMipmap(unsigned count)
+{
+   unsigned i, j;
+
+
+   /* ?? 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
 PerfNextRound(void)
@@ -334,34 +520,55 @@ PerfDraw(void)
 {
    GLint maxSize;
    double rate;
-   GLint mode;
-   double mbPerSec;
+   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 */
-   perf_printf("\n");
-   for (mode = 0; mode < MODE_COUNT; mode++) {
+   if (!g_csvstyle) perf_printf("\n");
+   for (test = 0; test < TEST_COUNT; test++) {
 
-      bytesPerImage = g_width * g_height * g_texelsize;
-      TexImage = malloc(bytesPerImage);
+      if (test_enable[test] == GL_FALSE) continue;
 
-      switch (mode) {
-      case MODE_TEXIMAGE:
+      switch (test) {
+      case TEST_TEXIMAGE:
+         bytesPerImage = g_width * g_height * g_texelsize;
+         TexImage = malloc(bytesPerImage);
          rate = PerfMeasureRate(UploadTexImage2D);
          break;
 
-      case MODE_CREATE_TEXIMAGE:
+      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;
+
       default:
          exit(1);
       }
 
-      mbPerSec = rate * bytesPerImage / (1024.0 * 1024.0);
+      mbPerSec = (rate * bytesPerImage) / (1024.0 * 1024.0);
+      mTPerSec = (rate * g_width * g_height) / (1024.0 * 1024.0);
       free(TexImage);
 
       {
@@ -373,16 +580,17 @@ PerfDraw(void)
          }
       }
 
-      if (g_csvstyle) {
-         perf_printf("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n",
-                     "width", "height", "level", "type", "texelsize", "format",
-                     "internalformat", "images/sec", "MB/sec", "MT/sec");
-         perf_printf("%d, %d, %d, %s, %d, %s, %s, %.1f, %.1f\n",
-                     g_width, g_height, g_level, typeName, g_texelsize, formatName,
-                     internalformatName, rate, mbPerSec);
+      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",
-                     mode_name[mode],
+                     test_name[test],
                      configName, g_width, g_height, rate, mbPerSec);
       }
    }
-- 
1.8.1.2



More information about the mesa-dev mailing list