92dc3fc52c
- Fix format (print/scanf) - Suppress unused argument warning messages (bonus) Change-Id: I05c7724d2aba6da1e82a86000e11f3a8fef4e728
792 lines
21 KiB
C++
792 lines
21 KiB
C++
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define ATRACE_TAG ATRACE_TAG_ALWAYS
|
|
|
|
#include <gui/GraphicBufferAlloc.h>
|
|
#include <gui/Surface.h>
|
|
#include <gui/SurfaceControl.h>
|
|
#include <gui/GLConsumer.h>
|
|
#include <gui/Surface.h>
|
|
#include <ui/Fence.h>
|
|
#include <utils/Trace.h>
|
|
|
|
#include <EGL/egl.h>
|
|
#include <GLES2/gl2.h>
|
|
|
|
#include <math.h>
|
|
#include <getopt.h>
|
|
|
|
#include "Flatland.h"
|
|
#include "GLHelper.h"
|
|
|
|
using namespace ::android;
|
|
|
|
static uint32_t g_SleepBetweenSamplesMs = 0;
|
|
static bool g_PresentToWindow = false;
|
|
static size_t g_BenchmarkNameLen = 0;
|
|
|
|
struct BenchmarkDesc {
|
|
// The name of the test.
|
|
const char* name;
|
|
|
|
// The dimensions of the space in which window layers are specified.
|
|
uint32_t width;
|
|
uint32_t height;
|
|
|
|
// The screen heights at which to run the test.
|
|
uint32_t runHeights[MAX_TEST_RUNS];
|
|
|
|
// The list of window layers.
|
|
LayerDesc layers[MAX_NUM_LAYERS];
|
|
};
|
|
|
|
static const BenchmarkDesc benchmarks[] = {
|
|
{ "16:10 Single Static Window",
|
|
2560, 1600, { 800, 1200, 1600, 2400 },
|
|
{
|
|
{ // Window
|
|
0, staticGradient, opaque,
|
|
0, 50, 2560, 1454,
|
|
},
|
|
{ // Status bar
|
|
0, staticGradient, opaque,
|
|
0, 0, 2560, 50,
|
|
},
|
|
{ // Navigation bar
|
|
0, staticGradient, opaque,
|
|
0, 1504, 2560, 96,
|
|
},
|
|
},
|
|
},
|
|
|
|
{ "3:2 Single Static Window",
|
|
2048, 1536, { 1536 },
|
|
{
|
|
{ // Window
|
|
0, staticGradient, opaque,
|
|
0, 50, 2048, 1440,
|
|
},
|
|
{ // Status bar
|
|
0, staticGradient, opaque,
|
|
0, 0, 2048, 50,
|
|
},
|
|
{ // Navigation bar
|
|
0, staticGradient, opaque,
|
|
0, 1440, 2048, 96,
|
|
},
|
|
},
|
|
},
|
|
|
|
{ "16:10 App -> Home Transition",
|
|
2560, 1600, { 800, 1200, 1600, 2400 },
|
|
{
|
|
{ // Wallpaper
|
|
0, staticGradient, opaque,
|
|
0, 50, 2560, 1454,
|
|
},
|
|
{ // Launcher
|
|
0, staticGradient, blend,
|
|
0, 50, 2560, 1454,
|
|
},
|
|
{ // Outgoing activity
|
|
0, staticGradient, blendShrink,
|
|
20, 70, 2520, 1414,
|
|
},
|
|
{ // Status bar
|
|
0, staticGradient, opaque,
|
|
0, 0, 2560, 50,
|
|
},
|
|
{ // Navigation bar
|
|
0, staticGradient, opaque,
|
|
0, 1504, 2560, 96,
|
|
},
|
|
},
|
|
},
|
|
|
|
{ "3:2 App -> Home Transition",
|
|
2048, 1536, { 1536 },
|
|
{
|
|
{ // Wallpaper
|
|
0, staticGradient, opaque,
|
|
0, 50, 2048, 1440,
|
|
},
|
|
{ // Launcher
|
|
0, staticGradient, blend,
|
|
0, 50, 2048, 1440,
|
|
},
|
|
{ // Outgoing activity
|
|
0, staticGradient, blendShrink,
|
|
20, 70, 2048, 1400,
|
|
},
|
|
{ // Status bar
|
|
0, staticGradient, opaque,
|
|
0, 0, 2048, 50,
|
|
},
|
|
{ // Navigation bar
|
|
0, staticGradient, opaque,
|
|
0, 1440, 2048, 96,
|
|
},
|
|
},
|
|
},
|
|
|
|
{ "16:10 SurfaceView -> Home Transition",
|
|
2560, 1600, { 800, 1200, 1600, 2400 },
|
|
{
|
|
{ // Wallpaper
|
|
0, staticGradient, opaque,
|
|
0, 50, 2560, 1454,
|
|
},
|
|
{ // Launcher
|
|
0, staticGradient, blend,
|
|
0, 50, 2560, 1454,
|
|
},
|
|
{ // Outgoing SurfaceView
|
|
0, staticGradient, blendShrink,
|
|
20, 70, 2520, 1414,
|
|
},
|
|
{ // Outgoing activity
|
|
0, staticGradient, blendShrink,
|
|
20, 70, 2520, 1414,
|
|
},
|
|
{ // Status bar
|
|
0, staticGradient, opaque,
|
|
0, 0, 2560, 50,
|
|
},
|
|
{ // Navigation bar
|
|
0, staticGradient, opaque,
|
|
0, 1504, 2560, 96,
|
|
},
|
|
},
|
|
},
|
|
|
|
{ "3:2 SurfaceView -> Home Transition",
|
|
2048, 1536, { 1536 },
|
|
{
|
|
{ // Wallpaper
|
|
0, staticGradient, opaque,
|
|
0, 50, 2048, 1440,
|
|
},
|
|
{ // Launcher
|
|
0, staticGradient, blend,
|
|
0, 50, 2048, 1440,
|
|
},
|
|
{ // Outgoing SurfaceView
|
|
0, staticGradient, blendShrink,
|
|
20, 70, 2048, 1400,
|
|
},
|
|
{ // Outgoing activity
|
|
0, staticGradient, blendShrink,
|
|
20, 70, 2048, 1400,
|
|
},
|
|
{ // Status bar
|
|
0, staticGradient, opaque,
|
|
0, 0, 2048, 50,
|
|
},
|
|
{ // Navigation bar
|
|
0, staticGradient, opaque,
|
|
0, 1440, 2048, 96,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
static const ShaderDesc shaders[] = {
|
|
{
|
|
name: "Blit",
|
|
vertexShader: {
|
|
"precision mediump float;",
|
|
"",
|
|
"attribute vec4 position;",
|
|
"attribute vec4 uv;",
|
|
"",
|
|
"varying vec4 texCoords;",
|
|
"",
|
|
"uniform mat4 objToNdc;",
|
|
"uniform mat4 uvToTex;",
|
|
"",
|
|
"void main() {",
|
|
" gl_Position = objToNdc * position;",
|
|
" texCoords = uvToTex * uv;",
|
|
"}",
|
|
},
|
|
fragmentShader: {
|
|
"#extension GL_OES_EGL_image_external : require",
|
|
"precision mediump float;",
|
|
"",
|
|
"varying vec4 texCoords;",
|
|
"",
|
|
"uniform samplerExternalOES blitSrc;",
|
|
"uniform vec4 modColor;",
|
|
"",
|
|
"void main() {",
|
|
" gl_FragColor = texture2D(blitSrc, texCoords.xy);",
|
|
" gl_FragColor *= modColor;",
|
|
"}",
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "Gradient",
|
|
vertexShader: {
|
|
"precision mediump float;",
|
|
"",
|
|
"attribute vec4 position;",
|
|
"attribute vec4 uv;",
|
|
"",
|
|
"varying float interp;",
|
|
"",
|
|
"uniform mat4 objToNdc;",
|
|
"uniform mat4 uvToInterp;",
|
|
"",
|
|
"void main() {",
|
|
" gl_Position = objToNdc * position;",
|
|
" interp = (uvToInterp * uv).x;",
|
|
"}",
|
|
},
|
|
fragmentShader: {
|
|
"precision mediump float;",
|
|
"",
|
|
"varying float interp;",
|
|
"",
|
|
"uniform vec4 color0;",
|
|
"uniform vec4 color1;",
|
|
"",
|
|
"uniform sampler2D ditherKernel;",
|
|
"uniform float invDitherKernelSize;",
|
|
"uniform float invDitherKernelSizeSq;",
|
|
"",
|
|
"void main() {",
|
|
" float dither = texture2D(ditherKernel,",
|
|
" gl_FragCoord.xy * invDitherKernelSize).a;",
|
|
" dither *= invDitherKernelSizeSq;",
|
|
" vec4 color = mix(color0, color1, clamp(interp, 0.0, 1.0));",
|
|
" gl_FragColor = color + vec4(dither, dither, dither, 0.0);",
|
|
"}",
|
|
},
|
|
},
|
|
};
|
|
|
|
class Layer {
|
|
|
|
public:
|
|
|
|
Layer() :
|
|
mFirstFrame(true),
|
|
mGLHelper(NULL),
|
|
mSurface(EGL_NO_SURFACE) {
|
|
}
|
|
|
|
bool setUp(const LayerDesc& desc, GLHelper* helper) {
|
|
bool result;
|
|
|
|
mDesc = desc;
|
|
mGLHelper = helper;
|
|
|
|
result = mGLHelper->createSurfaceTexture(mDesc.width, mDesc.height,
|
|
&mGLConsumer, &mSurface, &mTexName);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
mRenderer = desc.rendererFactory();
|
|
result = mRenderer->setUp(helper);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
mComposer = desc.composerFactory();
|
|
result = mComposer->setUp(desc, helper);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void tearDown() {
|
|
if (mComposer != NULL) {
|
|
mComposer->tearDown();
|
|
delete mComposer;
|
|
mComposer = NULL;
|
|
}
|
|
|
|
if (mRenderer != NULL) {
|
|
mRenderer->tearDown();
|
|
delete mRenderer;
|
|
mRenderer = NULL;
|
|
}
|
|
|
|
if (mSurface != EGL_NO_SURFACE) {
|
|
mGLHelper->destroySurface(&mSurface);
|
|
mGLConsumer->abandon();
|
|
}
|
|
mGLHelper = NULL;
|
|
mGLConsumer.clear();
|
|
}
|
|
|
|
bool render() {
|
|
return mRenderer->render(mSurface);
|
|
}
|
|
|
|
bool prepareComposition() {
|
|
status_t err;
|
|
|
|
err = mGLConsumer->updateTexImage();
|
|
if (err < 0) {
|
|
fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool compose() {
|
|
return mComposer->compose(mTexName, mGLConsumer);
|
|
}
|
|
|
|
private:
|
|
bool mFirstFrame;
|
|
|
|
LayerDesc mDesc;
|
|
|
|
GLHelper* mGLHelper;
|
|
|
|
GLuint mTexName;
|
|
sp<GLConsumer> mGLConsumer;
|
|
EGLSurface mSurface;
|
|
|
|
Renderer* mRenderer;
|
|
Composer* mComposer;
|
|
};
|
|
|
|
class BenchmarkRunner {
|
|
|
|
public:
|
|
|
|
BenchmarkRunner(const BenchmarkDesc& desc, size_t instance) :
|
|
mDesc(desc),
|
|
mInstance(instance),
|
|
mNumLayers(countLayers(desc)),
|
|
mGLHelper(NULL),
|
|
mSurface(EGL_NO_SURFACE),
|
|
mWindowSurface(EGL_NO_SURFACE) {
|
|
}
|
|
|
|
bool setUp() {
|
|
ATRACE_CALL();
|
|
|
|
bool result;
|
|
EGLint resulte;
|
|
|
|
float scaleFactor = float(mDesc.runHeights[mInstance]) /
|
|
float(mDesc.height);
|
|
uint32_t w = uint32_t(scaleFactor * float(mDesc.width));
|
|
uint32_t h = mDesc.runHeights[mInstance];
|
|
|
|
mGLHelper = new GLHelper();
|
|
result = mGLHelper->setUp(shaders, NELEMS(shaders));
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
GLuint texName;
|
|
result = mGLHelper->createSurfaceTexture(w, h, &mGLConsumer, &mSurface,
|
|
&texName);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < mNumLayers; i++) {
|
|
// Scale the layer to match the current screen size.
|
|
LayerDesc ld = mDesc.layers[i];
|
|
ld.x = int32_t(scaleFactor * float(ld.x));
|
|
ld.y = int32_t(scaleFactor * float(ld.y));
|
|
ld.width = uint32_t(scaleFactor * float(ld.width));
|
|
ld.height = uint32_t(scaleFactor * float(ld.height));
|
|
|
|
// Set up the layer.
|
|
result = mLayers[i].setUp(ld, mGLHelper);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (g_PresentToWindow) {
|
|
result = mGLHelper->createWindowSurface(w, h, &mSurfaceControl,
|
|
&mWindowSurface);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
result = doFrame(mWindowSurface);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void tearDown() {
|
|
ATRACE_CALL();
|
|
|
|
for (size_t i = 0; i < mNumLayers; i++) {
|
|
mLayers[i].tearDown();
|
|
}
|
|
|
|
if (mGLHelper != NULL) {
|
|
if (mWindowSurface != EGL_NO_SURFACE) {
|
|
mGLHelper->destroySurface(&mWindowSurface);
|
|
}
|
|
mGLHelper->destroySurface(&mSurface);
|
|
mGLConsumer->abandon();
|
|
mGLConsumer.clear();
|
|
mSurfaceControl.clear();
|
|
mGLHelper->tearDown();
|
|
delete mGLHelper;
|
|
mGLHelper = NULL;
|
|
}
|
|
}
|
|
|
|
nsecs_t run(uint32_t warmUpFrames, uint32_t totalFrames) {
|
|
ATRACE_CALL();
|
|
|
|
bool result;
|
|
status_t err;
|
|
|
|
resetColorGenerator();
|
|
|
|
// Do the warm-up frames.
|
|
for (uint32_t i = 0; i < warmUpFrames; i++) {
|
|
result = doFrame(mSurface);
|
|
if (!result) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Grab the fence for the start timestamp.
|
|
sp<Fence> startFence = mGLConsumer->getCurrentFence();
|
|
|
|
// the timed frames.
|
|
for (uint32_t i = warmUpFrames; i < totalFrames; i++) {
|
|
result = doFrame(mSurface);
|
|
if (!result) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Grab the fence for the end timestamp.
|
|
sp<Fence> endFence = mGLConsumer->getCurrentFence();
|
|
|
|
// Keep doing frames until the end fence has signaled.
|
|
while (endFence->wait(0) == -ETIME) {
|
|
result = doFrame(mSurface);
|
|
if (!result) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Compute the time delta.
|
|
nsecs_t startTime = startFence->getSignalTime();
|
|
nsecs_t endTime = endFence->getSignalTime();
|
|
|
|
return endTime - startTime;
|
|
}
|
|
|
|
private:
|
|
|
|
bool doFrame(EGLSurface surface) {
|
|
bool result;
|
|
status_t err;
|
|
|
|
for (size_t i = 0; i < mNumLayers; i++) {
|
|
result = mLayers[i].render();
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < mNumLayers; i++) {
|
|
result = mLayers[i].prepareComposition();
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
result = mGLHelper->makeCurrent(surface);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
for (size_t i = 0; i < mNumLayers; i++) {
|
|
result = mLayers[i].compose();
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
result = mGLHelper->swapBuffers(surface);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
|
|
err = mGLConsumer->updateTexImage();
|
|
if (err < 0) {
|
|
fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static size_t countLayers(const BenchmarkDesc& desc) {
|
|
size_t i;
|
|
for (i = 0; i < MAX_NUM_LAYERS; i++) {
|
|
if (desc.layers[i].rendererFactory == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
const BenchmarkDesc& mDesc;
|
|
const size_t mInstance;
|
|
const size_t mNumLayers;
|
|
|
|
GLHelper* mGLHelper;
|
|
|
|
// The surface into which layers are composited
|
|
sp<GLConsumer> mGLConsumer;
|
|
EGLSurface mSurface;
|
|
|
|
// Used for displaying the surface to a window.
|
|
EGLSurface mWindowSurface;
|
|
sp<SurfaceControl> mSurfaceControl;
|
|
|
|
Layer mLayers[MAX_NUM_LAYERS];
|
|
};
|
|
|
|
static int cmpDouble(const double* lhs, const double* rhs) {
|
|
if (*lhs < *rhs) {
|
|
return -1;
|
|
} else if (*rhs < *lhs) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Run a single benchmark and print the result.
|
|
static bool runTest(const BenchmarkDesc b, size_t run) {
|
|
bool success = true;
|
|
double prevResult = 0.0, result = 0.0;
|
|
Vector<double> samples;
|
|
|
|
uint32_t runHeight = b.runHeights[run];
|
|
uint32_t runWidth = b.width * runHeight / b.height;
|
|
printf(" %-*s | %4d x %4d | ", static_cast<int>(g_BenchmarkNameLen), b.name,
|
|
runWidth, runHeight);
|
|
fflush(stdout);
|
|
|
|
BenchmarkRunner r(b, run);
|
|
if (!r.setUp()) {
|
|
fprintf(stderr, "error initializing runner.\n");
|
|
return false;
|
|
}
|
|
|
|
// The slowest 1/outlierFraction sample results are ignored as potential
|
|
// outliers.
|
|
const uint32_t outlierFraction = 16;
|
|
const double threshold = .0025;
|
|
|
|
uint32_t warmUpFrames = 1;
|
|
uint32_t totalFrames = 5;
|
|
|
|
// Find the number of frames needed to run for over 100ms.
|
|
double runTime = 0.0;
|
|
while (true) {
|
|
runTime = double(r.run(warmUpFrames, totalFrames));
|
|
if (runTime < 50e6) {
|
|
warmUpFrames *= 2;
|
|
totalFrames *= 2;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (totalFrames - warmUpFrames > 16) {
|
|
// The test runs too fast to get a stable result. Skip it.
|
|
printf(" fast");
|
|
goto done;
|
|
} else if (totalFrames == 5 && runTime > 200e6) {
|
|
// The test runs too slow to be very useful. Skip it.
|
|
printf(" slow");
|
|
goto done;
|
|
}
|
|
|
|
do {
|
|
size_t newSamples = samples.size();
|
|
if (newSamples == 0) {
|
|
newSamples = 4*outlierFraction;
|
|
}
|
|
|
|
if (newSamples > 512) {
|
|
printf("varies");
|
|
goto done;
|
|
}
|
|
|
|
for (size_t i = 0; i < newSamples; i++) {
|
|
double sample = double(r.run(warmUpFrames, totalFrames));
|
|
|
|
if (g_SleepBetweenSamplesMs > 0) {
|
|
usleep(g_SleepBetweenSamplesMs * 1000);
|
|
}
|
|
|
|
if (sample < 0.0) {
|
|
success = false;
|
|
goto done;
|
|
}
|
|
|
|
samples.add(sample);
|
|
}
|
|
|
|
samples.sort(cmpDouble);
|
|
|
|
prevResult = result;
|
|
size_t elem = (samples.size() * (outlierFraction-1) / outlierFraction);
|
|
result = (samples[elem-1] + samples[elem]) * 0.5;
|
|
} while (fabs(result - prevResult) > threshold * result);
|
|
|
|
printf("%6.3f", result / double(totalFrames - warmUpFrames) / 1e6);
|
|
|
|
done:
|
|
|
|
printf("\n");
|
|
fflush(stdout);
|
|
r.tearDown();
|
|
|
|
return success;
|
|
}
|
|
|
|
static void printResultsTableHeader() {
|
|
const char* scenario = "Scenario";
|
|
size_t len = strlen(scenario);
|
|
size_t leftPad = (g_BenchmarkNameLen - len) / 2;
|
|
size_t rightPad = g_BenchmarkNameLen - len - leftPad;
|
|
printf(" %*s%s%*s | Resolution | Time (ms)\n",
|
|
static_cast<int>(leftPad), "",
|
|
"Scenario", static_cast<int>(rightPad), "");
|
|
}
|
|
|
|
// Run ALL the benchmarks!
|
|
static bool runTests() {
|
|
printResultsTableHeader();
|
|
|
|
for (size_t i = 0; i < NELEMS(benchmarks); i++) {
|
|
const BenchmarkDesc& b = benchmarks[i];
|
|
for (size_t j = 0; j < MAX_TEST_RUNS && b.runHeights[j]; j++) {
|
|
if (!runTest(b, j)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Return the length longest benchmark name.
|
|
static size_t maxBenchmarkNameLen() {
|
|
size_t maxLen = 0;
|
|
for (size_t i = 0; i < NELEMS(benchmarks); i++) {
|
|
const BenchmarkDesc& b = benchmarks[i];
|
|
size_t len = strlen(b.name);
|
|
if (len > maxLen) {
|
|
maxLen = len;
|
|
}
|
|
}
|
|
return maxLen;
|
|
}
|
|
|
|
// Print the command usage help to stderr.
|
|
static void showHelp(const char *cmd) {
|
|
fprintf(stderr, "usage: %s [options]\n", cmd);
|
|
fprintf(stderr, "options include:\n"
|
|
" -s N sleep for N ms between samples\n"
|
|
" -d display the test frame to a window\n"
|
|
" --help print this helpful message and exit\n"
|
|
);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
|
|
showHelp(argv[0]);
|
|
exit(0);
|
|
}
|
|
|
|
for (;;) {
|
|
int ret;
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
{"help", no_argument, 0, 0 },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
ret = getopt_long(argc, argv, "ds:",
|
|
long_options, &option_index);
|
|
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
|
|
switch(ret) {
|
|
case 'd':
|
|
g_PresentToWindow = true;
|
|
break;
|
|
|
|
case 's':
|
|
g_SleepBetweenSamplesMs = atoi(optarg);
|
|
break;
|
|
|
|
case 0:
|
|
if (strcmp(long_options[option_index].name, "help")) {
|
|
showHelp(argv[0]);
|
|
exit(0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
showHelp(argv[0]);
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
g_BenchmarkNameLen = maxBenchmarkNameLen();
|
|
|
|
printf(" cmdline:");
|
|
for (int i = 0; i < argc; i++) {
|
|
printf(" %s", argv[i]);
|
|
}
|
|
printf("\n");
|
|
|
|
if (!runTests()) {
|
|
fprintf(stderr, "exiting due to error.\n");
|
|
return 1;
|
|
}
|
|
}
|