diff options
Diffstat (limited to 'cmds/flatland/Main.cpp')
-rw-r--r-- | cmds/flatland/Main.cpp | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp new file mode 100644 index 0000000..99715d3 --- /dev/null +++ b/cmds/flatland/Main.cpp @@ -0,0 +1,716 @@ +/* + * 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, 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, + }, + }, + }, + + { "16:10 App -> Home Transition", + 2560, 1600, { 800, 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, + }, + }, + }, + + { "16:10 SurfaceView -> Home Transition", + 2560, 1600, { 800, 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, + }, + }, + }, +}; + +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 | ", 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", leftPad, "", + "Scenario", 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; + } +} |