flatland: add a GPU hardware benchmark
This change adds a GPU benchmark named 'flatland' that is intended to measure GPU performance of UI rendering and compositing scenarios at a fixed a clock frequency. This initial version includes only window compositing scenarios. Change-Id: I5577863aa3be5c6da8b49cb5d53cc49dec2f7081
This commit is contained in:
parent
2adaf04fab
commit
9c183f2493
22
cmds/flatland/Android.mk
Normal file
22
cmds/flatland/Android.mk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES:= \
|
||||||
|
Composers.cpp \
|
||||||
|
GLHelper.cpp \
|
||||||
|
Renderers.cpp \
|
||||||
|
Main.cpp \
|
||||||
|
|
||||||
|
LOCAL_MODULE:= flatland
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := tests
|
||||||
|
|
||||||
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
libEGL \
|
||||||
|
libGLESv2 \
|
||||||
|
libcutils \
|
||||||
|
libgui \
|
||||||
|
libui \
|
||||||
|
libutils \
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
282
cmds/flatland/Composers.cpp
Normal file
282
cmds/flatland/Composers.cpp
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Flatland.h"
|
||||||
|
#include "GLHelper.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class Blitter {
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool setUp(GLHelper* helper) {
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
result = helper->getShaderProgram("Blit", &mBlitPgm);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPosAttribLoc = glGetAttribLocation(mBlitPgm, "position");
|
||||||
|
mUVAttribLoc = glGetAttribLocation(mBlitPgm, "uv");
|
||||||
|
mUVToTexUniformLoc = glGetUniformLocation(mBlitPgm, "uvToTex");
|
||||||
|
mObjToNdcUniformLoc = glGetUniformLocation(mBlitPgm, "objToNdc");
|
||||||
|
mBlitSrcSamplerLoc = glGetUniformLocation(mBlitPgm, "blitSrc");
|
||||||
|
mModColorUniformLoc = glGetUniformLocation(mBlitPgm, "modColor");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blit(GLuint texName, const float* texMatrix,
|
||||||
|
int32_t x, int32_t y, uint32_t w, uint32_t h) {
|
||||||
|
float modColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
|
return modBlit(texName, texMatrix, modColor, x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool modBlit(GLuint texName, const float* texMatrix, float* modColor,
|
||||||
|
int32_t x, int32_t y, uint32_t w, uint32_t h) {
|
||||||
|
glUseProgram(mBlitPgm);
|
||||||
|
|
||||||
|
GLint vp[4];
|
||||||
|
glGetIntegerv(GL_VIEWPORT, vp);
|
||||||
|
float screenToNdc[16] = {
|
||||||
|
2.0f/float(vp[2]), 0.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, -2.0f/float(vp[3]), 0.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
const float pos[] = {
|
||||||
|
float(x), float(y),
|
||||||
|
float(x+w), float(y),
|
||||||
|
float(x), float(y+h),
|
||||||
|
float(x+w), float(y+h),
|
||||||
|
};
|
||||||
|
const float uv[] = {
|
||||||
|
0.0f, 0.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos);
|
||||||
|
glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv);
|
||||||
|
glEnableVertexAttribArray(mPosAttribLoc);
|
||||||
|
glEnableVertexAttribArray(mUVAttribLoc);
|
||||||
|
|
||||||
|
glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, screenToNdc);
|
||||||
|
glUniformMatrix4fv(mUVToTexUniformLoc, 1, GL_FALSE, texMatrix);
|
||||||
|
glUniform4fv(mModColorUniformLoc, 1, modColor);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName);
|
||||||
|
glUniform1i(mBlitSrcSamplerLoc, 0);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
glDisableVertexAttribArray(mPosAttribLoc);
|
||||||
|
glDisableVertexAttribArray(mUVAttribLoc);
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "GL error!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint mBlitPgm;
|
||||||
|
GLint mPosAttribLoc;
|
||||||
|
GLint mUVAttribLoc;
|
||||||
|
GLint mUVToTexUniformLoc;
|
||||||
|
GLint mObjToNdcUniformLoc;
|
||||||
|
GLint mBlitSrcSamplerLoc;
|
||||||
|
GLint mModColorUniformLoc;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComposerBase : public Composer {
|
||||||
|
public:
|
||||||
|
virtual ~ComposerBase() {}
|
||||||
|
|
||||||
|
virtual bool setUp(const LayerDesc& desc,
|
||||||
|
GLHelper* helper) {
|
||||||
|
mLayerDesc = desc;
|
||||||
|
return setUp(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void tearDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool setUp(GLHelper* helper) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerDesc mLayerDesc;
|
||||||
|
};
|
||||||
|
|
||||||
|
Composer* nocomp() {
|
||||||
|
class NoComp : public ComposerBase {
|
||||||
|
};
|
||||||
|
return new NoComp();
|
||||||
|
}
|
||||||
|
|
||||||
|
Composer* opaque() {
|
||||||
|
class OpaqueComp : public ComposerBase {
|
||||||
|
virtual bool setUp(GLHelper* helper) {
|
||||||
|
return mBlitter.setUp(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
|
||||||
|
float texMatrix[16];
|
||||||
|
glc->getTransformMatrix(texMatrix);
|
||||||
|
|
||||||
|
int32_t x = mLayerDesc.x;
|
||||||
|
int32_t y = mLayerDesc.y;
|
||||||
|
int32_t w = mLayerDesc.width;
|
||||||
|
int32_t h = mLayerDesc.height;
|
||||||
|
|
||||||
|
return mBlitter.blit(texName, texMatrix, x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
Blitter mBlitter;
|
||||||
|
};
|
||||||
|
return new OpaqueComp();
|
||||||
|
}
|
||||||
|
|
||||||
|
Composer* opaqueShrink() {
|
||||||
|
class OpaqueComp : public ComposerBase {
|
||||||
|
virtual bool setUp(GLHelper* helper) {
|
||||||
|
mParity = false;
|
||||||
|
return mBlitter.setUp(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
|
||||||
|
float texMatrix[16];
|
||||||
|
glc->getTransformMatrix(texMatrix);
|
||||||
|
|
||||||
|
int32_t x = mLayerDesc.x;
|
||||||
|
int32_t y = mLayerDesc.y;
|
||||||
|
int32_t w = mLayerDesc.width;
|
||||||
|
int32_t h = mLayerDesc.height;
|
||||||
|
|
||||||
|
mParity = !mParity;
|
||||||
|
if (mParity) {
|
||||||
|
x += w / 128;
|
||||||
|
y += h / 128;
|
||||||
|
w -= w / 64;
|
||||||
|
h -= h / 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mBlitter.blit(texName, texMatrix, x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
Blitter mBlitter;
|
||||||
|
bool mParity;
|
||||||
|
};
|
||||||
|
return new OpaqueComp();
|
||||||
|
}
|
||||||
|
|
||||||
|
Composer* blend() {
|
||||||
|
class BlendComp : public ComposerBase {
|
||||||
|
virtual bool setUp(GLHelper* helper) {
|
||||||
|
return mBlitter.setUp(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
float texMatrix[16];
|
||||||
|
glc->getTransformMatrix(texMatrix);
|
||||||
|
|
||||||
|
float modColor[4] = { .75f, .75f, .75f, .75f };
|
||||||
|
|
||||||
|
int32_t x = mLayerDesc.x;
|
||||||
|
int32_t y = mLayerDesc.y;
|
||||||
|
int32_t w = mLayerDesc.width;
|
||||||
|
int32_t h = mLayerDesc.height;
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
result = mBlitter.modBlit(texName, texMatrix, modColor,
|
||||||
|
x, y, w, h);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Blitter mBlitter;
|
||||||
|
};
|
||||||
|
return new BlendComp();
|
||||||
|
}
|
||||||
|
|
||||||
|
Composer* blendShrink() {
|
||||||
|
class BlendShrinkComp : public ComposerBase {
|
||||||
|
virtual bool setUp(GLHelper* helper) {
|
||||||
|
mParity = false;
|
||||||
|
return mBlitter.setUp(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
float texMatrix[16];
|
||||||
|
glc->getTransformMatrix(texMatrix);
|
||||||
|
|
||||||
|
float modColor[4] = { .75f, .75f, .75f, .75f };
|
||||||
|
|
||||||
|
int32_t x = mLayerDesc.x;
|
||||||
|
int32_t y = mLayerDesc.y;
|
||||||
|
int32_t w = mLayerDesc.width;
|
||||||
|
int32_t h = mLayerDesc.height;
|
||||||
|
|
||||||
|
mParity = !mParity;
|
||||||
|
if (mParity) {
|
||||||
|
x += w / 128;
|
||||||
|
y += h / 128;
|
||||||
|
w -= w / 64;
|
||||||
|
h -= h / 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
result = mBlitter.modBlit(texName, texMatrix, modColor,
|
||||||
|
x, y, w, h);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Blitter mBlitter;
|
||||||
|
bool mParity;
|
||||||
|
};
|
||||||
|
return new BlendShrinkComp();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
71
cmds/flatland/Flatland.h
Normal file
71
cmds/flatland/Flatland.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
|
||||||
|
#include <gui/GLConsumer.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
#define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
||||||
|
|
||||||
|
enum { MAX_NUM_LAYERS = 16 };
|
||||||
|
enum { MAX_TEST_RUNS = 16 };
|
||||||
|
|
||||||
|
class Composer;
|
||||||
|
class Renderer;
|
||||||
|
class GLHelper;
|
||||||
|
|
||||||
|
struct LayerDesc {
|
||||||
|
uint32_t flags;
|
||||||
|
Renderer* (*rendererFactory)();
|
||||||
|
Composer* (*composerFactory)();
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
void resetColorGenerator();
|
||||||
|
|
||||||
|
class Composer {
|
||||||
|
public:
|
||||||
|
virtual ~Composer() {}
|
||||||
|
virtual bool setUp(const LayerDesc& desc, GLHelper* helper) = 0;
|
||||||
|
virtual void tearDown() = 0;
|
||||||
|
virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Composer* nocomp();
|
||||||
|
Composer* opaque();
|
||||||
|
Composer* opaqueShrink();
|
||||||
|
Composer* blend();
|
||||||
|
Composer* blendShrink();
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
public:
|
||||||
|
virtual ~Renderer() {}
|
||||||
|
virtual bool setUp(GLHelper* helper) = 0;
|
||||||
|
virtual void tearDown() = 0;
|
||||||
|
virtual bool render(EGLSurface surface) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Renderer* staticGradient();
|
||||||
|
|
||||||
|
} // namespace android
|
461
cmds/flatland/GLHelper.cpp
Normal file
461
cmds/flatland/GLHelper.cpp
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ui/Fence.h>
|
||||||
|
|
||||||
|
#include <ui/DisplayInfo.h>
|
||||||
|
#include <gui/SurfaceComposerClient.h>
|
||||||
|
|
||||||
|
#include "GLHelper.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
GLHelper::GLHelper() :
|
||||||
|
mGraphicBufferAlloc(new GraphicBufferAlloc()),
|
||||||
|
mDisplay(EGL_NO_DISPLAY),
|
||||||
|
mContext(EGL_NO_CONTEXT),
|
||||||
|
mDummySurface(EGL_NO_SURFACE),
|
||||||
|
mConfig(0),
|
||||||
|
mShaderPrograms(NULL),
|
||||||
|
mDitherTexture(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
GLHelper::~GLHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) {
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
|
if (mDisplay == EGL_NO_DISPLAY) {
|
||||||
|
fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint majorVersion;
|
||||||
|
EGLint minorVersion;
|
||||||
|
result = eglInitialize(mDisplay, &majorVersion, &minorVersion);
|
||||||
|
if (result != EGL_TRUE) {
|
||||||
|
fprintf(stderr, "eglInitialize error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint numConfigs = 0;
|
||||||
|
EGLint configAttribs[] = {
|
||||||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||||
|
EGL_RED_SIZE, 8,
|
||||||
|
EGL_GREEN_SIZE, 8,
|
||||||
|
EGL_BLUE_SIZE, 8,
|
||||||
|
EGL_ALPHA_SIZE, 8,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
result = eglChooseConfig(mDisplay, configAttribs, &mConfig, 1,
|
||||||
|
&numConfigs);
|
||||||
|
if (result != EGL_TRUE) {
|
||||||
|
fprintf(stderr, "eglChooseConfig error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint contextAttribs[] = {
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT,
|
||||||
|
contextAttribs);
|
||||||
|
if (mContext == EGL_NO_CONTEXT) {
|
||||||
|
fprintf(stderr, "eglCreateContext error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resultb = createNamedSurfaceTexture(0, 1, 1, &mDummyGLConsumer,
|
||||||
|
&mDummySurface);
|
||||||
|
if (!resultb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultb = makeCurrent(mDummySurface);
|
||||||
|
if (!resultb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultb = setUpShaders(shaderDescs, numShaders);
|
||||||
|
if (!resultb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLHelper::tearDown() {
|
||||||
|
if (mShaderPrograms != NULL) {
|
||||||
|
delete[] mShaderPrograms;
|
||||||
|
mShaderPrograms = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSurfaceComposerClient != NULL) {
|
||||||
|
mSurfaceComposerClient->dispose();
|
||||||
|
mSurfaceComposerClient.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDisplay != EGL_NO_DISPLAY) {
|
||||||
|
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mContext != EGL_NO_CONTEXT) {
|
||||||
|
eglDestroyContext(mDisplay, mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDummySurface != EGL_NO_SURFACE) {
|
||||||
|
eglDestroySurface(mDisplay, mDummySurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDisplay = EGL_NO_DISPLAY;
|
||||||
|
mContext = EGL_NO_CONTEXT;
|
||||||
|
mDummySurface = EGL_NO_SURFACE;
|
||||||
|
mDummyGLConsumer.clear();
|
||||||
|
mConfig = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::makeCurrent(EGLSurface surface) {
|
||||||
|
EGLint result;
|
||||||
|
|
||||||
|
result = eglMakeCurrent(mDisplay, surface, surface, mContext);
|
||||||
|
if (result != EGL_TRUE) {
|
||||||
|
fprintf(stderr, "eglMakeCurrent error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint w, h;
|
||||||
|
eglQuerySurface(mDisplay, surface, EGL_WIDTH, &w);
|
||||||
|
eglQuerySurface(mDisplay, surface, EGL_HEIGHT, &h);
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::createSurfaceTexture(uint32_t w, uint32_t h,
|
||||||
|
sp<GLConsumer>* glConsumer, EGLSurface* surface,
|
||||||
|
GLuint* name) {
|
||||||
|
if (!makeCurrent(mDummySurface)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*name = 0;
|
||||||
|
glGenTextures(1, name);
|
||||||
|
if (*name == 0) {
|
||||||
|
fprintf(stderr, "glGenTextures error: %#x\n", glGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createNamedSurfaceTexture(*name, w, h, glConsumer, surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLHelper::destroySurface(EGLSurface* surface) {
|
||||||
|
if (eglGetCurrentSurface(EGL_READ) == *surface ||
|
||||||
|
eglGetCurrentSurface(EGL_DRAW) == *surface) {
|
||||||
|
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
eglDestroySurface(mDisplay, *surface);
|
||||||
|
*surface = EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::swapBuffers(EGLSurface surface) {
|
||||||
|
EGLint result;
|
||||||
|
result = eglSwapBuffers(mDisplay, surface);
|
||||||
|
if (result != EGL_TRUE) {
|
||||||
|
fprintf(stderr, "eglSwapBuffers error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) {
|
||||||
|
for (size_t i = 0; i < mNumShaders; i++) {
|
||||||
|
if (strcmp(mShaderDescs[i].name, name) == 0) {
|
||||||
|
*outPgm = mShaderPrograms[i];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "unknown shader name: \"%s\"\n", name);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
|
||||||
|
sp<GLConsumer>* glConsumer, EGLSurface* surface) {
|
||||||
|
sp<BufferQueue> bq = new BufferQueue(true, mGraphicBufferAlloc);
|
||||||
|
sp<GLConsumer> glc = new GLConsumer(name, true,
|
||||||
|
GL_TEXTURE_EXTERNAL_OES, false, bq);
|
||||||
|
glc->setDefaultBufferSize(w, h);
|
||||||
|
glc->setDefaultMaxBufferCount(3);
|
||||||
|
glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
|
||||||
|
|
||||||
|
sp<ANativeWindow> anw = new SurfaceTextureClient(bq);
|
||||||
|
EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL);
|
||||||
|
if (s == EGL_NO_SURFACE) {
|
||||||
|
fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*glConsumer = glc;
|
||||||
|
*surface = s;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
|
||||||
|
sp<IBinder> dpy = mSurfaceComposerClient->getBuiltInDisplay(0);
|
||||||
|
if (dpy == NULL) {
|
||||||
|
fprintf(stderr, "SurfaceComposer::getBuiltInDisplay failed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayInfo info;
|
||||||
|
status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float scaleX = float(info.w) / float(w);
|
||||||
|
float scaleY = float(info.h) / float(h);
|
||||||
|
*scale = scaleX < scaleY ? scaleX : scaleY;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::createWindowSurface(uint32_t w, uint32_t h,
|
||||||
|
sp<SurfaceControl>* surfaceControl, EGLSurface* surface) {
|
||||||
|
bool result;
|
||||||
|
status_t err;
|
||||||
|
|
||||||
|
if (mSurfaceComposerClient == NULL) {
|
||||||
|
mSurfaceComposerClient = new SurfaceComposerClient;
|
||||||
|
}
|
||||||
|
err = mSurfaceComposerClient->initCheck();
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<SurfaceControl> sc = mSurfaceComposerClient->createSurface(
|
||||||
|
String8("Benchmark"), w, h, PIXEL_FORMAT_RGBA_8888, 0);
|
||||||
|
if (sc == NULL || !sc->isValid()) {
|
||||||
|
fprintf(stderr, "Failed to create SurfaceControl.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float scale;
|
||||||
|
result = computeWindowScale(w, h, &scale);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceComposerClient::openGlobalTransaction();
|
||||||
|
err = sc->setLayer(0x7FFFFFFF);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
err = sc->setMatrix(scale, 0.0f, 0.0f, scale);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
fprintf(stderr, "SurfaceComposer::setMatrix error: %#x\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sc->show();
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
fprintf(stderr, "SurfaceComposer::show error: %#x\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SurfaceComposerClient::closeGlobalTransaction();
|
||||||
|
|
||||||
|
sp<ANativeWindow> anw = sc->getSurface();
|
||||||
|
EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL);
|
||||||
|
if (s == EGL_NO_SURFACE) {
|
||||||
|
fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*surfaceControl = sc;
|
||||||
|
*surface = s;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compileShader(GLenum shaderType, const char* src,
|
||||||
|
GLuint* outShader) {
|
||||||
|
GLuint shader = glCreateShader(shaderType);
|
||||||
|
if (shader == 0) {
|
||||||
|
fprintf(stderr, "glCreateShader error: %#x\n", glGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glShaderSource(shader, 1, &src, NULL);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
GLint compiled = 0;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
||||||
|
if (!compiled) {
|
||||||
|
GLint infoLen = 0;
|
||||||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
|
||||||
|
if (infoLen) {
|
||||||
|
char* buf = new char[infoLen];
|
||||||
|
if (buf) {
|
||||||
|
glGetShaderInfoLog(shader, infoLen, NULL, buf);
|
||||||
|
fprintf(stderr, "Shader compile log:\n%s\n", buf);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteShader(shader);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*outShader = shader;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printShaderSource(const char* const* src) {
|
||||||
|
for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) {
|
||||||
|
fprintf(stderr, "%3d: %s\n", i+1, src[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* makeShaderString(const char* const* src) {
|
||||||
|
size_t len = 0;
|
||||||
|
for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) {
|
||||||
|
// The +1 is for the '\n' that will be added.
|
||||||
|
len += strlen(src[i]) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* result = new char[len+1];
|
||||||
|
char* end = result;
|
||||||
|
for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) {
|
||||||
|
strcpy(end, src[i]);
|
||||||
|
end += strlen(src[i]);
|
||||||
|
*end = '\n';
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
*end = '\0';
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compileShaderLines(GLenum shaderType, const char* const* lines,
|
||||||
|
GLuint* outShader) {
|
||||||
|
const char* src = makeShaderString(lines);
|
||||||
|
bool result = compileShader(shaderType, src, outShader);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "Shader source:\n");
|
||||||
|
printShaderSource(lines);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delete[] src;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) {
|
||||||
|
GLuint program = glCreateProgram();
|
||||||
|
if (program == 0) {
|
||||||
|
fprintf(stderr, "glCreateProgram error: %#x\n", glGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glAttachShader(program, vs);
|
||||||
|
glAttachShader(program, fs);
|
||||||
|
glLinkProgram(program);
|
||||||
|
GLint linkStatus = GL_FALSE;
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||||
|
if (linkStatus != GL_TRUE) {
|
||||||
|
GLint bufLength = 0;
|
||||||
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
|
||||||
|
if (bufLength) {
|
||||||
|
char* buf = new char[bufLength];
|
||||||
|
if (buf) {
|
||||||
|
glGetProgramInfoLog(program, bufLength, NULL, buf);
|
||||||
|
fprintf(stderr, "Program link log:\n%s\n", buf);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteProgram(program);
|
||||||
|
program = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outPgm = program;
|
||||||
|
return program != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders) {
|
||||||
|
mShaderPrograms = new GLuint[numShaders];
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numShaders && result; i++) {
|
||||||
|
GLuint vs, fs;
|
||||||
|
|
||||||
|
result = compileShaderLines(GL_VERTEX_SHADER,
|
||||||
|
shaderDescs[i].vertexShader, &vs);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = compileShaderLines(GL_FRAGMENT_SHADER,
|
||||||
|
shaderDescs[i].fragmentShader, &fs);
|
||||||
|
if (!result) {
|
||||||
|
glDeleteShader(vs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = linkShaderProgram(vs, fs, &mShaderPrograms[i]);
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
mNumShaders = numShaders;
|
||||||
|
mShaderDescs = shaderDescs;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLHelper::getDitherTexture(GLuint* outTexName) {
|
||||||
|
if (mDitherTexture == 0) {
|
||||||
|
const uint8_t pattern[] = {
|
||||||
|
0, 8, 2, 10,
|
||||||
|
12, 4, 14, 6,
|
||||||
|
3, 11, 1, 9,
|
||||||
|
15, 7, 13, 5
|
||||||
|
};
|
||||||
|
|
||||||
|
glGenTextures(1, &mDitherTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mDitherTexture);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE,
|
||||||
|
DITHER_KERNEL_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
*outTexName = mDitherTexture;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
94
cmds/flatland/GLHelper.h
Normal file
94
cmds/flatland/GLHelper.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gui/GraphicBufferAlloc.h>
|
||||||
|
#include <gui/GLConsumer.h>
|
||||||
|
#include <gui/SurfaceTextureClient.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class SurfaceComposerClient;
|
||||||
|
class SurfaceControl;
|
||||||
|
|
||||||
|
enum { MAX_SHADER_LINES = 128 };
|
||||||
|
|
||||||
|
struct ShaderDesc {
|
||||||
|
const char* name;
|
||||||
|
const char* vertexShader[MAX_SHADER_LINES];
|
||||||
|
const char* fragmentShader[MAX_SHADER_LINES];
|
||||||
|
};
|
||||||
|
|
||||||
|
class GLHelper {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum { DITHER_KERNEL_SIZE = 4 };
|
||||||
|
|
||||||
|
GLHelper();
|
||||||
|
|
||||||
|
~GLHelper();
|
||||||
|
|
||||||
|
bool setUp(const ShaderDesc* shaderDescs, size_t numShaders);
|
||||||
|
|
||||||
|
void tearDown();
|
||||||
|
|
||||||
|
bool makeCurrent(EGLSurface surface);
|
||||||
|
|
||||||
|
bool createSurfaceTexture(uint32_t w, uint32_t h,
|
||||||
|
sp<GLConsumer>* surfaceTexture, EGLSurface* surface,
|
||||||
|
GLuint* name);
|
||||||
|
|
||||||
|
bool createWindowSurface(uint32_t w, uint32_t h,
|
||||||
|
sp<SurfaceControl>* surfaceControl, EGLSurface* surface);
|
||||||
|
|
||||||
|
void destroySurface(EGLSurface* surface);
|
||||||
|
|
||||||
|
bool swapBuffers(EGLSurface surface);
|
||||||
|
|
||||||
|
bool getShaderProgram(const char* name, GLuint* outPgm);
|
||||||
|
|
||||||
|
bool getDitherTexture(GLuint* outTexName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
|
||||||
|
sp<GLConsumer>* surfaceTexture, EGLSurface* surface);
|
||||||
|
|
||||||
|
bool computeWindowScale(uint32_t w, uint32_t h, float* scale);
|
||||||
|
|
||||||
|
bool setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders);
|
||||||
|
|
||||||
|
sp<GraphicBufferAlloc> mGraphicBufferAlloc;
|
||||||
|
|
||||||
|
EGLDisplay mDisplay;
|
||||||
|
EGLContext mContext;
|
||||||
|
EGLSurface mDummySurface;
|
||||||
|
sp<GLConsumer> mDummyGLConsumer;
|
||||||
|
EGLConfig mConfig;
|
||||||
|
|
||||||
|
sp<SurfaceComposerClient> mSurfaceComposerClient;
|
||||||
|
|
||||||
|
GLuint* mShaderPrograms;
|
||||||
|
const ShaderDesc* mShaderDescs;
|
||||||
|
size_t mNumShaders;
|
||||||
|
|
||||||
|
GLuint mDitherTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
715
cmds/flatland/Main.cpp
Normal file
715
cmds/flatland/Main.cpp
Normal file
@ -0,0 +1,715 @@
|
|||||||
|
/*
|
||||||
|
* 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/GLConsumer.h>
|
||||||
|
#include <gui/SurfaceTextureClient.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;
|
||||||
|
}
|
||||||
|
}
|
74
cmds/flatland/README.txt
Normal file
74
cmds/flatland/README.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
Flatland is a benchmark for measuring GPU performance in various 2D UI
|
||||||
|
rendering and window compositing scenarios. It is designed to be used early
|
||||||
|
in the device development process to evaluate GPU hardware (e.g. for SoC
|
||||||
|
selection). It uses OpenGL ES 2.0, gralloc, and the Android explicit
|
||||||
|
synchronization framework, so it can only be run on devices with drivers
|
||||||
|
supporting those HALs.
|
||||||
|
|
||||||
|
|
||||||
|
Preparing a Device
|
||||||
|
|
||||||
|
Because it's measuring hardware performance, flatland should be run in as
|
||||||
|
consistent and static an environment as possible. The display should be
|
||||||
|
turned off and background services should be stopped before running the
|
||||||
|
benchmark. Running 'adb shell stop' after turning off the display is probably
|
||||||
|
sufficient for this, but if there are device- specific background services
|
||||||
|
that consume much CPU cycles, memory bandwidth, or might otherwise interfere
|
||||||
|
with GPU rendering, those should be stopped as well (and ideally they'd be
|
||||||
|
fixed or eliminated for production devices).
|
||||||
|
|
||||||
|
Additionally, all relevant hardware clocks should be locked at a particular
|
||||||
|
frequency when running flatland. At a minimum this includes the CPU, GPU, and
|
||||||
|
memory bus clocks. Running flatland with dynamic clocking essentially
|
||||||
|
measures the behavior of the dynamic clocking algorithm under a fairly
|
||||||
|
unrealistic workload, and will likely result in unstable and useless results.
|
||||||
|
|
||||||
|
If running the benchmark with the clocks locked causes thermal issues, the -s
|
||||||
|
command line option can be used to insert a sleep (specified in milliseconds)
|
||||||
|
in between each benchmark sample run. Regardless of the scenario being
|
||||||
|
measured, each sample measurement runs for between 50 and 200 ms, so a sleep
|
||||||
|
time between 10 and 50 ms should address most thermal problems.
|
||||||
|
|
||||||
|
|
||||||
|
Interpreting the Output
|
||||||
|
|
||||||
|
The output of flatland should look something like this:
|
||||||
|
|
||||||
|
cmdline: flatland
|
||||||
|
Scenario | Resolution | Time (ms)
|
||||||
|
16:10 Single Static Window | 1280 x 800 | fast
|
||||||
|
16:10 Single Static Window | 2560 x 1600 | 5.368
|
||||||
|
16:10 Single Static Window | 3840 x 2400 | 11.979
|
||||||
|
16:10 App -> Home Transition | 1280 x 800 | 4.069
|
||||||
|
16:10 App -> Home Transition | 2560 x 1600 | 15.911
|
||||||
|
16:10 App -> Home Transition | 3840 x 2400 | 38.795
|
||||||
|
16:10 SurfaceView -> Home Transition | 1280 x 800 | 5.387
|
||||||
|
16:10 SurfaceView -> Home Transition | 2560 x 1600 | 21.147
|
||||||
|
16:10 SurfaceView -> Home Transition | 3840 x 2400 | slow
|
||||||
|
|
||||||
|
The first column is simply a description of the scenario that's being
|
||||||
|
simulated. The second column indicates the resolution at which the scenario
|
||||||
|
was measured. The third column is the measured benchmark result. It
|
||||||
|
indicates the expected time in milliseconds that a single frame of the
|
||||||
|
scenario takes to complete.
|
||||||
|
|
||||||
|
The third column may also contain one of three other values:
|
||||||
|
|
||||||
|
fast - This indicates that frames of the scenario completed too fast to be
|
||||||
|
reliably benchmarked. This corresponds to a frame time less than 3 ms.
|
||||||
|
Rather than spending time trying (and likely failing) to get a stable
|
||||||
|
result, the scenario was skipped.
|
||||||
|
|
||||||
|
slow - This indicates that frames of the scenario took too long to
|
||||||
|
complete. This corresponds to a frame time over 50 ms. Rather than
|
||||||
|
simulating a scenario that is obviously impractical on this device, the
|
||||||
|
scenario was skipped.
|
||||||
|
|
||||||
|
varies - This indicates that the scenario was measured, but it did not
|
||||||
|
yield a stable result. Occasionally this happens with an otherwise stable
|
||||||
|
scenario. In this case, simply rerunning flatland should yield a valid
|
||||||
|
result. If a scenario repeatedly results in a 'varies' output, that
|
||||||
|
probably indicates that something is wrong with the environment in which
|
||||||
|
flatland is being run. Check that the hardware clock frequencies are
|
||||||
|
locked and that no heavy-weight services / daemons are running in the
|
||||||
|
background.
|
197
cmds/flatland/Renderers.cpp
Normal file
197
cmds/flatland/Renderers.cpp
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Flatland.h"
|
||||||
|
#include "GLHelper.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
static float colors[][4] = {
|
||||||
|
{ .85f, .14f, .44f, 1.0f },
|
||||||
|
{ .91f, .72f, .10f, 1.0f },
|
||||||
|
{ .04f, .66f, .42f, 1.0f },
|
||||||
|
{ .84f, .39f, .68f, 1.0f },
|
||||||
|
{ .38f, .53f, .78f, 1.0f },
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t g_colorIndex;
|
||||||
|
|
||||||
|
const float* genColor() {
|
||||||
|
float* color = colors[g_colorIndex];
|
||||||
|
g_colorIndex = (g_colorIndex + 1) % NELEMS(colors);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetColorGenerator() {
|
||||||
|
g_colorIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GradientRenderer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool setUp(GLHelper* helper) {
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
result = helper->getShaderProgram("Gradient", &mGradPgm);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = helper->getDitherTexture(&mDitherTexName);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPosAttribLoc = glGetAttribLocation(mGradPgm, "position");
|
||||||
|
mUVAttribLoc = glGetAttribLocation(mGradPgm, "uv");
|
||||||
|
mUVToInterpUniformLoc = glGetUniformLocation(mGradPgm, "uvToInterp");
|
||||||
|
mObjToNdcUniformLoc = glGetUniformLocation(mGradPgm, "objToNdc");
|
||||||
|
mDitherKernelSamplerLoc = glGetUniformLocation(mGradPgm, "ditherKernel");
|
||||||
|
mInvDitherKernelSizeUniformLoc = glGetUniformLocation(mGradPgm,
|
||||||
|
"invDitherKernelSize");
|
||||||
|
mInvDitherKernelSizeSqUniformLoc = glGetUniformLocation(mGradPgm,
|
||||||
|
"invDitherKernelSizeSq");
|
||||||
|
mColor0UniformLoc = glGetUniformLocation(mGradPgm, "color0");
|
||||||
|
mColor1UniformLoc = glGetUniformLocation(mGradPgm, "color1");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool drawGradient() {
|
||||||
|
float identity[16] = {
|
||||||
|
1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, 1.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
const float pos[] = {
|
||||||
|
-1.0f, -1.0f,
|
||||||
|
1.0f, -1.0f,
|
||||||
|
-1.0f, 1.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
};
|
||||||
|
const float uv[] = {
|
||||||
|
0.0f, 0.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
};
|
||||||
|
const float* color0 = genColor();
|
||||||
|
const float* color1 = genColor();
|
||||||
|
|
||||||
|
glUseProgram(mGradPgm);
|
||||||
|
|
||||||
|
glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos);
|
||||||
|
glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv);
|
||||||
|
glEnableVertexAttribArray(mPosAttribLoc);
|
||||||
|
glEnableVertexAttribArray(mUVAttribLoc);
|
||||||
|
|
||||||
|
float invDitherKernelSize = 1.0f / float(GLHelper::DITHER_KERNEL_SIZE);
|
||||||
|
float invDitherKernelSizeSq = invDitherKernelSize * invDitherKernelSize;
|
||||||
|
|
||||||
|
glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, identity);
|
||||||
|
glUniformMatrix4fv(mUVToInterpUniformLoc, 1, GL_FALSE, identity);
|
||||||
|
glUniform1f(mInvDitherKernelSizeUniformLoc, invDitherKernelSize);
|
||||||
|
glUniform1f(mInvDitherKernelSizeSqUniformLoc, invDitherKernelSizeSq);
|
||||||
|
glUniform4fv(mColor0UniformLoc, 1, color0);
|
||||||
|
glUniform4fv(mColor1UniformLoc, 1, color1);
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "GL error! 0\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mDitherTexName);
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "GL error! 1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
glUniform1i(mDitherKernelSamplerLoc, 0);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
glDisableVertexAttribArray(mPosAttribLoc);
|
||||||
|
glDisableVertexAttribArray(mUVAttribLoc);
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR) {
|
||||||
|
fprintf(stderr, "GL error! 2\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint mGradPgm;
|
||||||
|
GLuint mDitherTexName;
|
||||||
|
GLuint mPosAttribLoc;
|
||||||
|
GLuint mUVAttribLoc;
|
||||||
|
GLuint mObjToNdcUniformLoc;
|
||||||
|
GLuint mUVToInterpUniformLoc;
|
||||||
|
GLuint mDitherKernelSamplerLoc;
|
||||||
|
GLuint mInvDitherKernelSizeUniformLoc;
|
||||||
|
GLuint mInvDitherKernelSizeSqUniformLoc;
|
||||||
|
GLuint mColor0UniformLoc;
|
||||||
|
GLuint mColor1UniformLoc;
|
||||||
|
};
|
||||||
|
|
||||||
|
Renderer* staticGradient() {
|
||||||
|
class NoRenderer : public Renderer {
|
||||||
|
virtual bool setUp(GLHelper* helper) {
|
||||||
|
mIsFirstFrame = true;
|
||||||
|
mGLHelper = helper;
|
||||||
|
return mGradientRenderer.setUp(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void tearDown() {
|
||||||
|
mGradientRenderer.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool render(EGLSurface surface) {
|
||||||
|
if (mIsFirstFrame) {
|
||||||
|
bool result;
|
||||||
|
mIsFirstFrame = false;
|
||||||
|
|
||||||
|
result = mGLHelper->makeCurrent(surface);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = mGradientRenderer.drawGradient();
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = mGLHelper->swapBuffers(surface);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mIsFirstFrame;
|
||||||
|
GLHelper* mGLHelper;
|
||||||
|
GradientRenderer mGradientRenderer;
|
||||||
|
};
|
||||||
|
return new NoRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace android
|
Loading…
Reference in New Issue
Block a user