16928bfeca
The shell property debug.egl.trace can now be set to: 0 disables tracing 1 logs all GL calls error checks glGetError after every GL call, logs a stack trace on error systrace logs each GL call to systrace Change-Id: I34a2a2d4e19c373fd9eaa1b0cd93e67c87378996
484 lines
16 KiB
C++
484 lines
16 KiB
C++
/*
|
|
** Copyright 2010, 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.
|
|
*/
|
|
|
|
#if EGL_TRACE
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <EGL/egl.h>
|
|
#include <EGL/eglext.h>
|
|
#include <GLES/gl.h>
|
|
#include <GLES/glext.h>
|
|
|
|
#include <cutils/log.h>
|
|
|
|
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
|
#include <utils/Trace.h>
|
|
|
|
#include <utils/CallStack.h>
|
|
|
|
#include "egl_tls.h"
|
|
#include "hooks.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
namespace android {
|
|
// ----------------------------------------------------------------------------
|
|
|
|
struct GLenumString {
|
|
GLenum e;
|
|
const char* s;
|
|
};
|
|
|
|
#undef GL_ENUM
|
|
#define GL_ENUM(VAL,NAME) {VAL, #NAME},
|
|
|
|
static GLenumString g_enumnames[] = {
|
|
#include "enums.in"
|
|
};
|
|
#undef GL_ENUM
|
|
|
|
static int compareGLEnum(const void* a, const void* b) {
|
|
return ((const GLenumString*) a)->e - ((const GLenumString*) b)->e;
|
|
}
|
|
|
|
static const char* GLEnumToString(GLenum e) {
|
|
GLenumString key = {e, ""};
|
|
const GLenumString* result = (const GLenumString*) bsearch(
|
|
&key, g_enumnames,
|
|
sizeof(g_enumnames) / sizeof(g_enumnames[0]),
|
|
sizeof(g_enumnames[0]), compareGLEnum);
|
|
if (result) {
|
|
return result->s;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const char* GLbooleanToString(GLboolean arg) {
|
|
return arg ? "GL_TRUE" : "GL_FALSE";
|
|
}
|
|
|
|
static GLenumString g_bitfieldNames[] = {
|
|
{0x00004000, "GL_COLOR_BUFFER_BIT"},
|
|
{0x00000400, "GL_STENCIL_BUFFER_BIT"},
|
|
{0x00000100, "GL_DEPTH_BUFFER_BIT"}
|
|
};
|
|
|
|
class StringBuilder {
|
|
static const int lineSize = 500;
|
|
char line[lineSize];
|
|
int line_index;
|
|
public:
|
|
StringBuilder() {
|
|
line_index = 0;
|
|
line[0] = '\0';
|
|
}
|
|
void append(const char* fmt, ...) {
|
|
va_list argp;
|
|
va_start(argp, fmt);
|
|
line_index += vsnprintf(line + line_index, lineSize-line_index, fmt, argp);
|
|
va_end(argp);
|
|
}
|
|
const char* getString() {
|
|
line_index = 0;
|
|
line[lineSize-1] = '\0';
|
|
return line;
|
|
}
|
|
};
|
|
|
|
|
|
static void TraceGLShaderSource(GLuint shader, GLsizei count,
|
|
const GLchar** string, const GLint* length) {
|
|
ALOGD("const char* shaderSrc[] = {");
|
|
for (GLsizei i = 0; i < count; i++) {
|
|
const char* comma = i < count-1 ? "," : "";
|
|
const GLchar* s = string[i];
|
|
if (length) {
|
|
GLint len = length[i];
|
|
ALOGD(" \"%*s\"%s", len, s, comma);
|
|
} else {
|
|
ALOGD(" \"%s\"%s", s, comma);
|
|
}
|
|
}
|
|
ALOGD("};");
|
|
if (length) {
|
|
ALOGD("const GLint* shaderLength[] = {");
|
|
for (GLsizei i = 0; i < count; i++) {
|
|
const char* comma = i < count-1 ? "," : "";
|
|
GLint len = length[i];
|
|
ALOGD(" \"%d\"%s", len, comma);
|
|
}
|
|
ALOGD("};");
|
|
ALOGD("glShaderSource(%u, %u, shaderSrc, shaderLength);",
|
|
shader, count);
|
|
} else {
|
|
ALOGD("glShaderSource(%u, %u, shaderSrc, (const GLint*) 0);",
|
|
shader, count);
|
|
}
|
|
}
|
|
|
|
static void TraceValue(int elementCount, char type,
|
|
GLsizei chunkCount, GLsizei chunkSize, const void* value) {
|
|
StringBuilder stringBuilder;
|
|
GLsizei count = chunkCount * chunkSize;
|
|
bool isFloat = type == 'f';
|
|
const char* typeString = isFloat ? "GLfloat" : "GLint";
|
|
ALOGD("const %s value[] = {", typeString);
|
|
for (GLsizei i = 0; i < count; i++) {
|
|
StringBuilder builder;
|
|
builder.append(" ");
|
|
for (int e = 0; e < elementCount; e++) {
|
|
const char* comma = ", ";
|
|
if (e == elementCount-1) {
|
|
if (i == count - 1) {
|
|
comma = "";
|
|
} else {
|
|
comma = ",";
|
|
}
|
|
}
|
|
if (isFloat) {
|
|
builder.append("%g%s", * (GLfloat*) value, comma);
|
|
value = (void*) (((GLfloat*) value) + 1);
|
|
} else {
|
|
builder.append("%d%s", * (GLint*) value, comma);
|
|
value = (void*) (((GLint*) value) + 1);
|
|
}
|
|
}
|
|
ALOGD("%s", builder.getString());
|
|
if (chunkSize > 1 && i < count-1
|
|
&& (i % chunkSize) == (chunkSize-1)) {
|
|
ALOGD("%s", ""); // Print a blank line.
|
|
}
|
|
}
|
|
ALOGD("};");
|
|
}
|
|
|
|
static void TraceUniformv(int elementCount, char type,
|
|
GLuint location, GLsizei count, const void* value) {
|
|
TraceValue(elementCount, type, count, 1, value);
|
|
ALOGD("glUniform%d%c(%u, %u, value);", elementCount, type, location, count);
|
|
}
|
|
|
|
static void TraceUniformMatrix(int matrixSideLength,
|
|
GLuint location, GLsizei count, GLboolean transpose, const void* value) {
|
|
TraceValue(matrixSideLength, 'f', count, matrixSideLength, value);
|
|
ALOGD("glUniformMatrix%dfv(%u, %u, %s, value);", matrixSideLength, location, count,
|
|
GLbooleanToString(transpose));
|
|
}
|
|
|
|
static void TraceGL(const char* name, int numArgs, ...) {
|
|
va_list argp;
|
|
va_start(argp, numArgs);
|
|
int nameLen = strlen(name);
|
|
|
|
// glShaderSource
|
|
if (nameLen == 14 && strcmp(name, "glShaderSource") == 0) {
|
|
va_arg(argp, const char*);
|
|
GLuint shader = va_arg(argp, GLuint);
|
|
va_arg(argp, const char*);
|
|
GLsizei count = va_arg(argp, GLsizei);
|
|
va_arg(argp, const char*);
|
|
const GLchar** string = (const GLchar**) va_arg(argp, void*);
|
|
va_arg(argp, const char*);
|
|
const GLint* length = (const GLint*) va_arg(argp, void*);
|
|
va_end(argp);
|
|
TraceGLShaderSource(shader, count, string, length);
|
|
return;
|
|
}
|
|
|
|
// glUniformXXv
|
|
|
|
if (nameLen == 12 && strncmp(name, "glUniform", 9) == 0 && name[11] == 'v') {
|
|
int elementCount = name[9] - '0'; // 1..4
|
|
char type = name[10]; // 'f' or 'i'
|
|
va_arg(argp, const char*);
|
|
GLuint location = va_arg(argp, GLuint);
|
|
va_arg(argp, const char*);
|
|
GLsizei count = va_arg(argp, GLsizei);
|
|
va_arg(argp, const char*);
|
|
const void* value = (const void*) va_arg(argp, void*);
|
|
va_end(argp);
|
|
TraceUniformv(elementCount, type, location, count, value);
|
|
return;
|
|
}
|
|
|
|
// glUniformMatrixXfv
|
|
|
|
if (nameLen == 18 && strncmp(name, "glUniformMatrix", 15) == 0
|
|
&& name[16] == 'f' && name[17] == 'v') {
|
|
int matrixSideLength = name[15] - '0'; // 2..4
|
|
va_arg(argp, const char*);
|
|
GLuint location = va_arg(argp, GLuint);
|
|
va_arg(argp, const char*);
|
|
GLsizei count = va_arg(argp, GLsizei);
|
|
va_arg(argp, const char*);
|
|
GLboolean transpose = (GLboolean) va_arg(argp, int);
|
|
va_arg(argp, const char*);
|
|
const void* value = (const void*) va_arg(argp, void*);
|
|
va_end(argp);
|
|
TraceUniformMatrix(matrixSideLength, location, count, transpose, value);
|
|
return;
|
|
}
|
|
|
|
StringBuilder builder;
|
|
builder.append("%s(", name);
|
|
for (int i = 0; i < numArgs; i++) {
|
|
if (i > 0) {
|
|
builder.append(", ");
|
|
}
|
|
const char* type = va_arg(argp, const char*);
|
|
bool isPtr = type[strlen(type)-1] == '*'
|
|
|| strcmp(type, "GLeglImageOES") == 0;
|
|
if (isPtr) {
|
|
const void* arg = va_arg(argp, const void*);
|
|
builder.append("(%s) 0x%08x", type, (size_t) arg);
|
|
} else if (strcmp(type, "GLbitfield") == 0) {
|
|
size_t arg = va_arg(argp, size_t);
|
|
bool first = true;
|
|
for (size_t i = 0; i < sizeof(g_bitfieldNames) / sizeof(g_bitfieldNames[0]); i++) {
|
|
const GLenumString* b = &g_bitfieldNames[i];
|
|
if (b->e & arg) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
builder.append(" | ");
|
|
}
|
|
builder.append("%s", b->s);
|
|
arg &= ~b->e;
|
|
}
|
|
}
|
|
if (first || arg != 0) {
|
|
if (!first) {
|
|
builder.append(" | ");
|
|
}
|
|
builder.append("0x%08x", arg);
|
|
}
|
|
} else if (strcmp(type, "GLboolean") == 0) {
|
|
GLboolean arg = va_arg(argp, int);
|
|
builder.append("%s", GLbooleanToString(arg));
|
|
} else if (strcmp(type, "GLclampf") == 0) {
|
|
double arg = va_arg(argp, double);
|
|
builder.append("%g", arg);
|
|
} else if (strcmp(type, "GLenum") == 0) {
|
|
GLenum arg = va_arg(argp, int);
|
|
const char* s = GLEnumToString(arg);
|
|
if (s) {
|
|
builder.append("%s", s);
|
|
} else {
|
|
builder.append("0x%x", arg);
|
|
}
|
|
} else if (strcmp(type, "GLfixed") == 0) {
|
|
int arg = va_arg(argp, int);
|
|
builder.append("0x%08x", arg);
|
|
} else if (strcmp(type, "GLfloat") == 0) {
|
|
double arg = va_arg(argp, double);
|
|
builder.append("%g", arg);
|
|
} else if (strcmp(type, "GLint") == 0) {
|
|
int arg = va_arg(argp, int);
|
|
const char* s = NULL;
|
|
if (strcmp(name, "glTexParameteri") == 0) {
|
|
s = GLEnumToString(arg);
|
|
}
|
|
if (s) {
|
|
builder.append("%s", s);
|
|
} else {
|
|
builder.append("%d", arg);
|
|
}
|
|
} else if (strcmp(type, "GLintptr") == 0) {
|
|
int arg = va_arg(argp, unsigned int);
|
|
builder.append("%u", arg);
|
|
} else if (strcmp(type, "GLsizei") == 0) {
|
|
int arg = va_arg(argp, size_t);
|
|
builder.append("%u", arg);
|
|
} else if (strcmp(type, "GLsizeiptr") == 0) {
|
|
int arg = va_arg(argp, size_t);
|
|
builder.append("%u", arg);
|
|
} else if (strcmp(type, "GLuint") == 0) {
|
|
int arg = va_arg(argp, unsigned int);
|
|
builder.append("%u", arg);
|
|
} else {
|
|
builder.append("/* ??? %s */", type);
|
|
break;
|
|
}
|
|
}
|
|
builder.append(");");
|
|
ALOGD("%s", builder.getString());
|
|
va_end(argp);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Log trace
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
#define TRACE_GL_VOID(_api, _args, _argList, ...) \
|
|
static void Tracing_ ## _api _args { \
|
|
TraceGL(#_api, __VA_ARGS__); \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
_c->_api _argList; \
|
|
}
|
|
|
|
#define TRACE_GL(_type, _api, _args, _argList, ...) \
|
|
static _type Tracing_ ## _api _args { \
|
|
TraceGL(#_api, __VA_ARGS__); \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
return _c->_api _argList; \
|
|
}
|
|
|
|
extern "C" {
|
|
#include "../trace.in"
|
|
}
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
#define GL_ENTRY(_r, _api, ...) Tracing_ ## _api,
|
|
EGLAPI gl_hooks_t gHooksTrace = {
|
|
{
|
|
#include "entries.in"
|
|
},
|
|
{
|
|
{0}
|
|
}
|
|
};
|
|
#undef GL_ENTRY
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
// define the ES 1.0 Debug_gl* functions as Tracing_gl functions
|
|
#define TRACE_GL_VOID(_api, _args, _argList, ...) \
|
|
static void Debug_ ## _api _args { \
|
|
TraceGL(#_api, __VA_ARGS__); \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
_c->_api _argList; \
|
|
}
|
|
|
|
#define TRACE_GL(_type, _api, _args, _argList, ...) \
|
|
static _type Debug_ ## _api _args { \
|
|
TraceGL(#_api, __VA_ARGS__); \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
return _c->_api _argList; \
|
|
}
|
|
|
|
extern "C" {
|
|
#include "../debug.in"
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Systrace
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
#define TRACE_GL_VOID(_api, _args, _argList, ...) \
|
|
static void Systrace_ ## _api _args { \
|
|
ATRACE_NAME(#_api); \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
_c->_api _argList; \
|
|
}
|
|
|
|
#define TRACE_GL(_type, _api, _args, _argList, ...) \
|
|
static _type Systrace_ ## _api _args { \
|
|
ATRACE_NAME(#_api); \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
return _c->_api _argList; \
|
|
}
|
|
|
|
extern "C" {
|
|
#include "../trace.in"
|
|
}
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
#define GL_ENTRY(_r, _api, ...) Systrace_ ## _api,
|
|
EGLAPI gl_hooks_t gHooksSystrace = {
|
|
{
|
|
#include "entries.in"
|
|
},
|
|
{
|
|
{0}
|
|
}
|
|
};
|
|
#undef GL_ENTRY
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
#define CHECK_ERROR(_c, _api) \
|
|
GLenum status = GL_NO_ERROR; \
|
|
bool error = false; \
|
|
while ((status = _c->glGetError()) != GL_NO_ERROR) { \
|
|
ALOGD("[" #_api "] 0x%x", status); \
|
|
error = true; \
|
|
} \
|
|
if (error) { \
|
|
CallStack s; \
|
|
s.update(); \
|
|
s.dump("glGetError:" #_api); \
|
|
} \
|
|
|
|
#define TRACE_GL_VOID(_api, _args, _argList, ...) \
|
|
static void ErrorTrace_ ## _api _args { \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
_c->_api _argList; \
|
|
CHECK_ERROR(_c, _api); \
|
|
}
|
|
|
|
#define TRACE_GL(_type, _api, _args, _argList, ...) \
|
|
static _type ErrorTrace_ ## _api _args { \
|
|
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \
|
|
_type _r = _c->_api _argList; \
|
|
CHECK_ERROR(_c, _api); \
|
|
return _r; \
|
|
}
|
|
|
|
extern "C" {
|
|
#include "../trace.in"
|
|
}
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
#define GL_ENTRY(_r, _api, ...) ErrorTrace_ ## _api,
|
|
EGLAPI gl_hooks_t gHooksErrorTrace = {
|
|
{
|
|
#include "entries.in"
|
|
},
|
|
{
|
|
{0}
|
|
}
|
|
};
|
|
#undef GL_ENTRY
|
|
#undef CHECK_ERROR
|
|
|
|
#undef TRACE_GL_VOID
|
|
#undef TRACE_GL
|
|
|
|
// ----------------------------------------------------------------------------
|
|
}; // namespace android
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#endif // EGL_TRACE
|