GLES2Debugger: Added DbgContext and vertex data capturing.

Send VBO related commands to client, which tracks the state.
Maintain index buffer content and vertex attrib pointer/buffer state on server.
During glDrawArrays/Elements, send user memory data to client.

Change-Id: Ia920e90479329b301ae4b5735e833eeb20293c94
Signed-off-by: David Li <davidxli@google.com>
This commit is contained in:
David Li 2011-03-10 16:40:37 -08:00
parent d625d10792
commit 65948aa046
17 changed files with 532 additions and 1999 deletions

View File

@ -30,6 +30,7 @@
#include "egl_impl.h"
#include "Loader.h"
#include "glesv2dbg.h"
// ----------------------------------------------------------------------------
namespace android {
@ -114,7 +115,6 @@ Loader::Loader()
Loader::~Loader()
{
extern void StopDebugServer();
StopDebugServer();
}

View File

@ -45,6 +45,7 @@
#include "hooks.h"
#include "egl_impl.h"
#include "Loader.h"
#include "glesv2dbg.h"
#define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
@ -223,9 +224,15 @@ struct egl_context_t : public egl_object_t
egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
int impl, egl_connection_t const* cnx, int version)
: dpy(dpy), context(context), config(config), read(0), draw(0), impl(impl),
cnx(cnx), version(version)
cnx(cnx), version(version), dbg(NULL)
{
}
~egl_context_t()
{
if (dbg)
DestroyDbgContext(dbg);
dbg = NULL;
}
EGLDisplay dpy;
EGLContext context;
EGLConfig config;
@ -234,6 +241,7 @@ struct egl_context_t : public egl_object_t
int impl;
egl_connection_t const* cnx;
int version;
DbgContext * dbg;
};
struct egl_image_t : public egl_object_t
@ -325,14 +333,12 @@ static void initEglTraceLevel() {
char cmdline[256] = {};
if (fgets(cmdline, sizeof(cmdline) - 1, file))
{
LOGD("\n*\n*\n* initEglTraceLevel cmdline='%s' \n*\n*", cmdline);
if (!strcmp(value, cmdline))
gEGLDebugLevel = 1;
}
fclose(file);
}
extern void StartDebugServer();
if (gEGLDebugLevel > 0)
StartDebugServer();
}
@ -341,7 +347,7 @@ static void setGLHooksThreadSpecific(gl_hooks_t const *value) {
if (gEGLTraceLevel > 0) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(&gHooksTrace);
} else if (gEGLDebugLevel > 0) {
} else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(&gHooksDebug);
LOGD("\n* setGLHooksThreadSpecific gHooksDebug");
@ -586,6 +592,11 @@ egl_context_t* get_context(EGLContext context) {
return egl_to_native_cast<egl_context_t>(context);
}
DbgContext * getDbgContextThreadSpecific()
{
return get_context(getContext())->dbg;
}
static inline
egl_image_t* get_image(EGLImageKHR image) {
return egl_to_native_cast<egl_image_t>(image);
@ -1393,6 +1404,8 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
loseCurrent(cur_c);
if (ctx != EGL_NO_CONTEXT) {
if (!c->dbg && gEGLDebugLevel > 0)
c->dbg = CreateDbgContext(c->version, c->cnx->hooks[c->version]);
setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
setContext(ctx);
_c.acquire();

View File

@ -376,7 +376,7 @@ extern "C" {
// declare all Debug_gl* functions
#define GL_ENTRY(_r, _api, ...) _r Debug_##_api ( __VA_ARGS__ );
#include "../GLES2_dbg/include/glesv2_dbg.h"
#include "glesv2dbg_functions.h"
#undef GL_ENTRY
#define GL_ENTRY(_r, _api, ...) Debug_ ## _api,

View File

@ -4,10 +4,10 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
src/api.cpp \
src/dbgcontext.cpp \
src/debugger_message.pb.cpp \
src/egl.cpp \
src/server.cpp \
src/shader.cpp \
src/texture.cpp \
src/vertex.cpp

View File

@ -31,7 +31,16 @@ def generate_api(lines):
externs = []
i = 0
# these have been hand written
skipFunctions = ["glTexImage2D", "glTexSubImage2D", "glShaderSource", "glReadPixels", "glDrawArrays", "glDrawElements"]
skipFunctions = ["glTexImage2D", "glTexSubImage2D", "glReadPixels",
"glDrawArrays", "glDrawElements"]
# these have an EXTEND_Debug_* macro for getting data
extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glShaderSource"]
# these also needs to be forwarded to DbgContext
contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray",
"glVertexAttribPointer", "glBindBuffer", "glBufferData", "glBufferSubData", "glDeleteBuffers",]
for line in lines:
if line.find("API_ENTRY(") >= 0: # a function prototype
returnType = line[0: line.find(" API_ENTRY(")]
@ -49,12 +58,13 @@ def generate_api(lines):
parameters = parameterList.split(',')
paramIndex = 0
if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")):
# add function to list of functions that should be hand written, but generate code anyways
extern = "%s Debug_%s(%s);" % (returnType, functionName, RemoveAnnotation(parameterList))
sys.stderr.write("%s should be hand written\n" % (extern))
print "// FIXME: this function has pointers, it should be hand written"
externs.append(extern)
if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")): # unannotated pointer
if not functionName in extendFunctions:
# add function to list of functions that should be hand written, but generate code anyways
extern = "%s Debug_%s(%s);" % (returnType, functionName, RemoveAnnotation(parameterList))
sys.stderr.write("%s should be hand written\n" % (extern))
print "// FIXME: this function has pointers, it should be hand written"
externs.append(extern)
print "%s Debug_%s(%s)\n{" % (returnType, functionName, RemoveAnnotation(parameterList))
print """ glesv2debugger::Message msg;
@ -132,6 +142,8 @@ def generate_api(lines):
if inout in ["out", "inout"]:
print " msg.set_time((systemTime(timeMode) - c0) * 1e-6f);"
print " " + getData
if functionName in contextFunctions:
print " getDbgContextThreadSpecific()->%s(%s);" % (functionName, arguments)
if returnType == "void":
print " return 0;"
else:
@ -145,6 +157,8 @@ def generate_api(lines):
print " // FIXME: check for pointer usage"
if inout in ["in", "inout"]:
print getData
if functionName in extendFunctions:
print " EXTEND_Debug_%s;" % (functionName)
print " int * ret = MessageLoop(caller, msg, expectResponse,"
print " glesv2debugger::Message_Function_%s);" % (functionName)
if returnType != "void":
@ -160,7 +174,7 @@ def generate_api(lines):
print extern
if __name__ == "__main__":
print """
print """\
/*
** Copyright 2011, The Android Open Source Project
**
@ -179,7 +193,8 @@ if __name__ == "__main__":
// auto generated by generate_api_cpp.py
#include "src/header.h"
#include "src/header.h"
#include "src/api.h"
template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }

View File

@ -39,7 +39,7 @@ def generate_gl_entries(output,lines,i):
if __name__ == "__main__":
output = open("debugger_message.proto",'w')
output.write("""
output.write("""\
/*
* Copyright (C) 2011 The Android Open Source Project
*

File diff suppressed because it is too large Load Diff

View File

@ -1,844 +0,0 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: DebuggerMessage.proto
#ifndef PROTOBUF_DebuggerMessage_2eproto__INCLUDED
#define PROTOBUF_DebuggerMessage_2eproto__INCLUDED
#include <string>
#include <google/protobuf/stubs/common.h>
#if GOOGLE_PROTOBUF_VERSION < 2003000
#error This file was generated by a newer version of protoc which is
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
#if 2003000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
#endif
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
// @@protoc_insertion_point(includes)
namespace GLESv2Debugger {
// Internal implementation detail -- do not call these.
void protobuf_AddDesc_DebuggerMessage_2eproto();
void protobuf_AssignDesc_DebuggerMessage_2eproto();
void protobuf_ShutdownFile_DebuggerMessage_2eproto();
class Message;
enum Message_Function {
Message_Function_glActiveTexture = 0,
Message_Function_glAttachShader = 1,
Message_Function_glBindAttribLocation = 2,
Message_Function_glBindBuffer = 3,
Message_Function_glBindFramebuffer = 4,
Message_Function_glBindRenderbuffer = 5,
Message_Function_glBindTexture = 6,
Message_Function_glBlendColor = 7,
Message_Function_glBlendEquation = 8,
Message_Function_glBlendEquationSeparate = 9,
Message_Function_glBlendFunc = 10,
Message_Function_glBlendFuncSeparate = 11,
Message_Function_glBufferData = 12,
Message_Function_glBufferSubData = 13,
Message_Function_glCheckFramebufferStatus = 14,
Message_Function_glClear = 15,
Message_Function_glClearColor = 16,
Message_Function_glClearDepthf = 17,
Message_Function_glClearStencil = 18,
Message_Function_glColorMask = 19,
Message_Function_glCompileShader = 20,
Message_Function_glCompressedTexImage2D = 21,
Message_Function_glCompressedTexSubImage2D = 22,
Message_Function_glCopyTexImage2D = 23,
Message_Function_glCopyTexSubImage2D = 24,
Message_Function_glCreateProgram = 25,
Message_Function_glCreateShader = 26,
Message_Function_glCullFace = 27,
Message_Function_glDeleteBuffers = 28,
Message_Function_glDeleteFramebuffers = 29,
Message_Function_glDeleteProgram = 30,
Message_Function_glDeleteRenderbuffers = 31,
Message_Function_glDeleteShader = 32,
Message_Function_glDeleteTextures = 33,
Message_Function_glDepthFunc = 34,
Message_Function_glDepthMask = 35,
Message_Function_glDepthRangef = 36,
Message_Function_glDetachShader = 37,
Message_Function_glDisable = 38,
Message_Function_glDisableVertexAttribArray = 39,
Message_Function_glDrawArrays = 40,
Message_Function_glDrawElements = 41,
Message_Function_glEnable = 42,
Message_Function_glEnableVertexAttribArray = 43,
Message_Function_glFinish = 44,
Message_Function_glFlush = 45,
Message_Function_glFramebufferRenderbuffer = 46,
Message_Function_glFramebufferTexture2D = 47,
Message_Function_glFrontFace = 48,
Message_Function_glGenBuffers = 49,
Message_Function_glGenerateMipmap = 50,
Message_Function_glGenFramebuffers = 51,
Message_Function_glGenRenderbuffers = 52,
Message_Function_glGenTextures = 53,
Message_Function_glGetActiveAttrib = 54,
Message_Function_glGetActiveUniform = 55,
Message_Function_glGetAttachedShaders = 56,
Message_Function_glGetAttribLocation = 57,
Message_Function_glGetBooleanv = 58,
Message_Function_glGetBufferParameteriv = 59,
Message_Function_glGetError = 60,
Message_Function_glGetFloatv = 61,
Message_Function_glGetFramebufferAttachmentParameteriv = 62,
Message_Function_glGetIntegerv = 63,
Message_Function_glGetProgramiv = 64,
Message_Function_glGetProgramInfoLog = 65,
Message_Function_glGetRenderbufferParameteriv = 66,
Message_Function_glGetShaderiv = 67,
Message_Function_glGetShaderInfoLog = 68,
Message_Function_glGetShaderPrecisionFormat = 69,
Message_Function_glGetShaderSource = 70,
Message_Function_glGetString = 71,
Message_Function_glGetTexParameterfv = 72,
Message_Function_glGetTexParameteriv = 73,
Message_Function_glGetUniformfv = 74,
Message_Function_glGetUniformiv = 75,
Message_Function_glGetUniformLocation = 76,
Message_Function_glGetVertexAttribfv = 77,
Message_Function_glGetVertexAttribiv = 78,
Message_Function_glGetVertexAttribPointerv = 79,
Message_Function_glHint = 80,
Message_Function_glIsBuffer = 81,
Message_Function_glIsEnabled = 82,
Message_Function_glIsFramebuffer = 83,
Message_Function_glIsProgram = 84,
Message_Function_glIsRenderbuffer = 85,
Message_Function_glIsShader = 86,
Message_Function_glIsTexture = 87,
Message_Function_glLineWidth = 88,
Message_Function_glLinkProgram = 89,
Message_Function_glPixelStorei = 90,
Message_Function_glPolygonOffset = 91,
Message_Function_glReadPixels = 92,
Message_Function_glReleaseShaderCompiler = 93,
Message_Function_glRenderbufferStorage = 94,
Message_Function_glSampleCoverage = 95,
Message_Function_glScissor = 96,
Message_Function_glShaderBinary = 97,
Message_Function_glShaderSource = 98,
Message_Function_glStencilFunc = 99,
Message_Function_glStencilFuncSeparate = 100,
Message_Function_glStencilMask = 101,
Message_Function_glStencilMaskSeparate = 102,
Message_Function_glStencilOp = 103,
Message_Function_glStencilOpSeparate = 104,
Message_Function_glTexImage2D = 105,
Message_Function_glTexParameterf = 106,
Message_Function_glTexParameterfv = 107,
Message_Function_glTexParameteri = 108,
Message_Function_glTexParameteriv = 109,
Message_Function_glTexSubImage2D = 110,
Message_Function_glUniform1f = 111,
Message_Function_glUniform1fv = 112,
Message_Function_glUniform1i = 113,
Message_Function_glUniform1iv = 114,
Message_Function_glUniform2f = 115,
Message_Function_glUniform2fv = 116,
Message_Function_glUniform2i = 117,
Message_Function_glUniform2iv = 118,
Message_Function_glUniform3f = 119,
Message_Function_glUniform3fv = 120,
Message_Function_glUniform3i = 121,
Message_Function_glUniform3iv = 122,
Message_Function_glUniform4f = 123,
Message_Function_glUniform4fv = 124,
Message_Function_glUniform4i = 125,
Message_Function_glUniform4iv = 126,
Message_Function_glUniformMatrix2fv = 127,
Message_Function_glUniformMatrix3fv = 128,
Message_Function_glUniformMatrix4fv = 129,
Message_Function_glUseProgram = 130,
Message_Function_glValidateProgram = 131,
Message_Function_glVertexAttrib1f = 132,
Message_Function_glVertexAttrib1fv = 133,
Message_Function_glVertexAttrib2f = 134,
Message_Function_glVertexAttrib2fv = 135,
Message_Function_glVertexAttrib3f = 136,
Message_Function_glVertexAttrib3fv = 137,
Message_Function_glVertexAttrib4f = 138,
Message_Function_glVertexAttrib4fv = 139,
Message_Function_glVertexAttribPointer = 140,
Message_Function_glViewport = 141,
Message_Function_ACK = 142,
Message_Function_NEG = 143,
Message_Function_CONTINUE = 144,
Message_Function_SKIP = 145
};
bool Message_Function_IsValid(int value);
const Message_Function Message_Function_Function_MIN = Message_Function_glActiveTexture;
const Message_Function Message_Function_Function_MAX = Message_Function_SKIP;
const int Message_Function_Function_ARRAYSIZE = Message_Function_Function_MAX + 1;
// ===================================================================
class Message : public ::google::protobuf::MessageLite {
public:
Message();
virtual ~Message();
Message(const Message& from);
inline Message& operator=(const Message& from) {
CopyFrom(from);
return *this;
}
static const Message& default_instance();
void Swap(Message* other);
// implements Message ----------------------------------------------
Message* New() const;
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
void CopyFrom(const Message& from);
void MergeFrom(const Message& from);
void Clear();
bool IsInitialized() const;
int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const;
int GetCachedSize() const { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const;
public:
::std::string GetTypeName() const;
// nested types ----------------------------------------------------
typedef Message_Function Function;
static const Function glActiveTexture = Message_Function_glActiveTexture;
static const Function glAttachShader = Message_Function_glAttachShader;
static const Function glBindAttribLocation = Message_Function_glBindAttribLocation;
static const Function glBindBuffer = Message_Function_glBindBuffer;
static const Function glBindFramebuffer = Message_Function_glBindFramebuffer;
static const Function glBindRenderbuffer = Message_Function_glBindRenderbuffer;
static const Function glBindTexture = Message_Function_glBindTexture;
static const Function glBlendColor = Message_Function_glBlendColor;
static const Function glBlendEquation = Message_Function_glBlendEquation;
static const Function glBlendEquationSeparate = Message_Function_glBlendEquationSeparate;
static const Function glBlendFunc = Message_Function_glBlendFunc;
static const Function glBlendFuncSeparate = Message_Function_glBlendFuncSeparate;
static const Function glBufferData = Message_Function_glBufferData;
static const Function glBufferSubData = Message_Function_glBufferSubData;
static const Function glCheckFramebufferStatus = Message_Function_glCheckFramebufferStatus;
static const Function glClear = Message_Function_glClear;
static const Function glClearColor = Message_Function_glClearColor;
static const Function glClearDepthf = Message_Function_glClearDepthf;
static const Function glClearStencil = Message_Function_glClearStencil;
static const Function glColorMask = Message_Function_glColorMask;
static const Function glCompileShader = Message_Function_glCompileShader;
static const Function glCompressedTexImage2D = Message_Function_glCompressedTexImage2D;
static const Function glCompressedTexSubImage2D = Message_Function_glCompressedTexSubImage2D;
static const Function glCopyTexImage2D = Message_Function_glCopyTexImage2D;
static const Function glCopyTexSubImage2D = Message_Function_glCopyTexSubImage2D;
static const Function glCreateProgram = Message_Function_glCreateProgram;
static const Function glCreateShader = Message_Function_glCreateShader;
static const Function glCullFace = Message_Function_glCullFace;
static const Function glDeleteBuffers = Message_Function_glDeleteBuffers;
static const Function glDeleteFramebuffers = Message_Function_glDeleteFramebuffers;
static const Function glDeleteProgram = Message_Function_glDeleteProgram;
static const Function glDeleteRenderbuffers = Message_Function_glDeleteRenderbuffers;
static const Function glDeleteShader = Message_Function_glDeleteShader;
static const Function glDeleteTextures = Message_Function_glDeleteTextures;
static const Function glDepthFunc = Message_Function_glDepthFunc;
static const Function glDepthMask = Message_Function_glDepthMask;
static const Function glDepthRangef = Message_Function_glDepthRangef;
static const Function glDetachShader = Message_Function_glDetachShader;
static const Function glDisable = Message_Function_glDisable;
static const Function glDisableVertexAttribArray = Message_Function_glDisableVertexAttribArray;
static const Function glDrawArrays = Message_Function_glDrawArrays;
static const Function glDrawElements = Message_Function_glDrawElements;
static const Function glEnable = Message_Function_glEnable;
static const Function glEnableVertexAttribArray = Message_Function_glEnableVertexAttribArray;
static const Function glFinish = Message_Function_glFinish;
static const Function glFlush = Message_Function_glFlush;
static const Function glFramebufferRenderbuffer = Message_Function_glFramebufferRenderbuffer;
static const Function glFramebufferTexture2D = Message_Function_glFramebufferTexture2D;
static const Function glFrontFace = Message_Function_glFrontFace;
static const Function glGenBuffers = Message_Function_glGenBuffers;
static const Function glGenerateMipmap = Message_Function_glGenerateMipmap;
static const Function glGenFramebuffers = Message_Function_glGenFramebuffers;
static const Function glGenRenderbuffers = Message_Function_glGenRenderbuffers;
static const Function glGenTextures = Message_Function_glGenTextures;
static const Function glGetActiveAttrib = Message_Function_glGetActiveAttrib;
static const Function glGetActiveUniform = Message_Function_glGetActiveUniform;
static const Function glGetAttachedShaders = Message_Function_glGetAttachedShaders;
static const Function glGetAttribLocation = Message_Function_glGetAttribLocation;
static const Function glGetBooleanv = Message_Function_glGetBooleanv;
static const Function glGetBufferParameteriv = Message_Function_glGetBufferParameteriv;
static const Function glGetError = Message_Function_glGetError;
static const Function glGetFloatv = Message_Function_glGetFloatv;
static const Function glGetFramebufferAttachmentParameteriv = Message_Function_glGetFramebufferAttachmentParameteriv;
static const Function glGetIntegerv = Message_Function_glGetIntegerv;
static const Function glGetProgramiv = Message_Function_glGetProgramiv;
static const Function glGetProgramInfoLog = Message_Function_glGetProgramInfoLog;
static const Function glGetRenderbufferParameteriv = Message_Function_glGetRenderbufferParameteriv;
static const Function glGetShaderiv = Message_Function_glGetShaderiv;
static const Function glGetShaderInfoLog = Message_Function_glGetShaderInfoLog;
static const Function glGetShaderPrecisionFormat = Message_Function_glGetShaderPrecisionFormat;
static const Function glGetShaderSource = Message_Function_glGetShaderSource;
static const Function glGetString = Message_Function_glGetString;
static const Function glGetTexParameterfv = Message_Function_glGetTexParameterfv;
static const Function glGetTexParameteriv = Message_Function_glGetTexParameteriv;
static const Function glGetUniformfv = Message_Function_glGetUniformfv;
static const Function glGetUniformiv = Message_Function_glGetUniformiv;
static const Function glGetUniformLocation = Message_Function_glGetUniformLocation;
static const Function glGetVertexAttribfv = Message_Function_glGetVertexAttribfv;
static const Function glGetVertexAttribiv = Message_Function_glGetVertexAttribiv;
static const Function glGetVertexAttribPointerv = Message_Function_glGetVertexAttribPointerv;
static const Function glHint = Message_Function_glHint;
static const Function glIsBuffer = Message_Function_glIsBuffer;
static const Function glIsEnabled = Message_Function_glIsEnabled;
static const Function glIsFramebuffer = Message_Function_glIsFramebuffer;
static const Function glIsProgram = Message_Function_glIsProgram;
static const Function glIsRenderbuffer = Message_Function_glIsRenderbuffer;
static const Function glIsShader = Message_Function_glIsShader;
static const Function glIsTexture = Message_Function_glIsTexture;
static const Function glLineWidth = Message_Function_glLineWidth;
static const Function glLinkProgram = Message_Function_glLinkProgram;
static const Function glPixelStorei = Message_Function_glPixelStorei;
static const Function glPolygonOffset = Message_Function_glPolygonOffset;
static const Function glReadPixels = Message_Function_glReadPixels;
static const Function glReleaseShaderCompiler = Message_Function_glReleaseShaderCompiler;
static const Function glRenderbufferStorage = Message_Function_glRenderbufferStorage;
static const Function glSampleCoverage = Message_Function_glSampleCoverage;
static const Function glScissor = Message_Function_glScissor;
static const Function glShaderBinary = Message_Function_glShaderBinary;
static const Function glShaderSource = Message_Function_glShaderSource;
static const Function glStencilFunc = Message_Function_glStencilFunc;
static const Function glStencilFuncSeparate = Message_Function_glStencilFuncSeparate;
static const Function glStencilMask = Message_Function_glStencilMask;
static const Function glStencilMaskSeparate = Message_Function_glStencilMaskSeparate;
static const Function glStencilOp = Message_Function_glStencilOp;
static const Function glStencilOpSeparate = Message_Function_glStencilOpSeparate;
static const Function glTexImage2D = Message_Function_glTexImage2D;
static const Function glTexParameterf = Message_Function_glTexParameterf;
static const Function glTexParameterfv = Message_Function_glTexParameterfv;
static const Function glTexParameteri = Message_Function_glTexParameteri;
static const Function glTexParameteriv = Message_Function_glTexParameteriv;
static const Function glTexSubImage2D = Message_Function_glTexSubImage2D;
static const Function glUniform1f = Message_Function_glUniform1f;
static const Function glUniform1fv = Message_Function_glUniform1fv;
static const Function glUniform1i = Message_Function_glUniform1i;
static const Function glUniform1iv = Message_Function_glUniform1iv;
static const Function glUniform2f = Message_Function_glUniform2f;
static const Function glUniform2fv = Message_Function_glUniform2fv;
static const Function glUniform2i = Message_Function_glUniform2i;
static const Function glUniform2iv = Message_Function_glUniform2iv;
static const Function glUniform3f = Message_Function_glUniform3f;
static const Function glUniform3fv = Message_Function_glUniform3fv;
static const Function glUniform3i = Message_Function_glUniform3i;
static const Function glUniform3iv = Message_Function_glUniform3iv;
static const Function glUniform4f = Message_Function_glUniform4f;
static const Function glUniform4fv = Message_Function_glUniform4fv;
static const Function glUniform4i = Message_Function_glUniform4i;
static const Function glUniform4iv = Message_Function_glUniform4iv;
static const Function glUniformMatrix2fv = Message_Function_glUniformMatrix2fv;
static const Function glUniformMatrix3fv = Message_Function_glUniformMatrix3fv;
static const Function glUniformMatrix4fv = Message_Function_glUniformMatrix4fv;
static const Function glUseProgram = Message_Function_glUseProgram;
static const Function glValidateProgram = Message_Function_glValidateProgram;
static const Function glVertexAttrib1f = Message_Function_glVertexAttrib1f;
static const Function glVertexAttrib1fv = Message_Function_glVertexAttrib1fv;
static const Function glVertexAttrib2f = Message_Function_glVertexAttrib2f;
static const Function glVertexAttrib2fv = Message_Function_glVertexAttrib2fv;
static const Function glVertexAttrib3f = Message_Function_glVertexAttrib3f;
static const Function glVertexAttrib3fv = Message_Function_glVertexAttrib3fv;
static const Function glVertexAttrib4f = Message_Function_glVertexAttrib4f;
static const Function glVertexAttrib4fv = Message_Function_glVertexAttrib4fv;
static const Function glVertexAttribPointer = Message_Function_glVertexAttribPointer;
static const Function glViewport = Message_Function_glViewport;
static const Function ACK = Message_Function_ACK;
static const Function NEG = Message_Function_NEG;
static const Function CONTINUE = Message_Function_CONTINUE;
static const Function SKIP = Message_Function_SKIP;
static inline bool Function_IsValid(int value) {
return Message_Function_IsValid(value);
}
static const Function Function_MIN =
Message_Function_Function_MIN;
static const Function Function_MAX =
Message_Function_Function_MAX;
static const int Function_ARRAYSIZE =
Message_Function_Function_ARRAYSIZE;
// accessors -------------------------------------------------------
// required int32 context_id = 1;
inline bool has_context_id() const;
inline void clear_context_id();
static const int kContextIdFieldNumber = 1;
inline ::google::protobuf::int32 context_id() const;
inline void set_context_id(::google::protobuf::int32 value);
// required .GLESv2Debugger.Message.Function function = 2 [default = NEG];
inline bool has_function() const;
inline void clear_function();
static const int kFunctionFieldNumber = 2;
inline ::GLESv2Debugger::Message_Function function() const;
inline void set_function(::GLESv2Debugger::Message_Function value);
// required bool has_next_message = 3;
inline bool has_has_next_message() const;
inline void clear_has_next_message();
static const int kHasNextMessageFieldNumber = 3;
inline bool has_next_message() const;
inline void set_has_next_message(bool value);
// required bool expect_response = 4;
inline bool has_expect_response() const;
inline void clear_expect_response();
static const int kExpectResponseFieldNumber = 4;
inline bool expect_response() const;
inline void set_expect_response(bool value);
// optional int32 ret = 5;
inline bool has_ret() const;
inline void clear_ret();
static const int kRetFieldNumber = 5;
inline ::google::protobuf::int32 ret() const;
inline void set_ret(::google::protobuf::int32 value);
// optional int32 arg0 = 6;
inline bool has_arg0() const;
inline void clear_arg0();
static const int kArg0FieldNumber = 6;
inline ::google::protobuf::int32 arg0() const;
inline void set_arg0(::google::protobuf::int32 value);
// optional int32 arg1 = 7;
inline bool has_arg1() const;
inline void clear_arg1();
static const int kArg1FieldNumber = 7;
inline ::google::protobuf::int32 arg1() const;
inline void set_arg1(::google::protobuf::int32 value);
// optional int32 arg2 = 8;
inline bool has_arg2() const;
inline void clear_arg2();
static const int kArg2FieldNumber = 8;
inline ::google::protobuf::int32 arg2() const;
inline void set_arg2(::google::protobuf::int32 value);
// optional int32 arg3 = 9;
inline bool has_arg3() const;
inline void clear_arg3();
static const int kArg3FieldNumber = 9;
inline ::google::protobuf::int32 arg3() const;
inline void set_arg3(::google::protobuf::int32 value);
// optional int32 arg4 = 16;
inline bool has_arg4() const;
inline void clear_arg4();
static const int kArg4FieldNumber = 16;
inline ::google::protobuf::int32 arg4() const;
inline void set_arg4(::google::protobuf::int32 value);
// optional int32 arg5 = 17;
inline bool has_arg5() const;
inline void clear_arg5();
static const int kArg5FieldNumber = 17;
inline ::google::protobuf::int32 arg5() const;
inline void set_arg5(::google::protobuf::int32 value);
// optional int32 arg6 = 18;
inline bool has_arg6() const;
inline void clear_arg6();
static const int kArg6FieldNumber = 18;
inline ::google::protobuf::int32 arg6() const;
inline void set_arg6(::google::protobuf::int32 value);
// optional int32 arg7 = 19;
inline bool has_arg7() const;
inline void clear_arg7();
static const int kArg7FieldNumber = 19;
inline ::google::protobuf::int32 arg7() const;
inline void set_arg7(::google::protobuf::int32 value);
// optional int32 arg8 = 20;
inline bool has_arg8() const;
inline void clear_arg8();
static const int kArg8FieldNumber = 20;
inline ::google::protobuf::int32 arg8() const;
inline void set_arg8(::google::protobuf::int32 value);
// optional bytes data = 10;
inline bool has_data() const;
inline void clear_data();
static const int kDataFieldNumber = 10;
inline const ::std::string& data() const;
inline void set_data(const ::std::string& value);
inline void set_data(const char* value);
inline void set_data(const void* value, size_t size);
inline ::std::string* mutable_data();
// optional float time = 11;
inline bool has_time() const;
inline void clear_time();
static const int kTimeFieldNumber = 11;
inline float time() const;
inline void set_time(float value);
// @@protoc_insertion_point(class_scope:GLESv2Debugger.Message)
private:
mutable int _cached_size_;
::google::protobuf::int32 context_id_;
int function_;
bool has_next_message_;
bool expect_response_;
::google::protobuf::int32 ret_;
::google::protobuf::int32 arg0_;
::google::protobuf::int32 arg1_;
::google::protobuf::int32 arg2_;
::google::protobuf::int32 arg3_;
::google::protobuf::int32 arg4_;
::google::protobuf::int32 arg5_;
::google::protobuf::int32 arg6_;
::google::protobuf::int32 arg7_;
::google::protobuf::int32 arg8_;
::std::string* data_;
static const ::std::string _default_data_;
float time_;
friend void protobuf_AddDesc_DebuggerMessage_2eproto();
friend void protobuf_AssignDesc_DebuggerMessage_2eproto();
friend void protobuf_ShutdownFile_DebuggerMessage_2eproto();
::google::protobuf::uint32 _has_bits_[(16 + 31) / 32];
// WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
inline bool _has_bit(int index) const {
return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;
}
inline void _set_bit(int index) {
_has_bits_[index / 32] |= (1u << (index % 32));
}
inline void _clear_bit(int index) {
_has_bits_[index / 32] &= ~(1u << (index % 32));
}
void InitAsDefaultInstance();
static Message* default_instance_;
};
// ===================================================================
// ===================================================================
// Message
// required int32 context_id = 1;
inline bool Message::has_context_id() const {
return _has_bit(0);
}
inline void Message::clear_context_id() {
context_id_ = 0;
_clear_bit(0);
}
inline ::google::protobuf::int32 Message::context_id() const {
return context_id_;
}
inline void Message::set_context_id(::google::protobuf::int32 value) {
_set_bit(0);
context_id_ = value;
}
// required .GLESv2Debugger.Message.Function function = 2 [default = NEG];
inline bool Message::has_function() const {
return _has_bit(1);
}
inline void Message::clear_function() {
function_ = 143;
_clear_bit(1);
}
inline ::GLESv2Debugger::Message_Function Message::function() const {
return static_cast< ::GLESv2Debugger::Message_Function >(function_);
}
inline void Message::set_function(::GLESv2Debugger::Message_Function value) {
GOOGLE_DCHECK(::GLESv2Debugger::Message_Function_IsValid(value));
_set_bit(1);
function_ = value;
}
// required bool has_next_message = 3;
inline bool Message::has_has_next_message() const {
return _has_bit(2);
}
inline void Message::clear_has_next_message() {
has_next_message_ = false;
_clear_bit(2);
}
inline bool Message::has_next_message() const {
return has_next_message_;
}
inline void Message::set_has_next_message(bool value) {
_set_bit(2);
has_next_message_ = value;
}
// required bool expect_response = 4;
inline bool Message::has_expect_response() const {
return _has_bit(3);
}
inline void Message::clear_expect_response() {
expect_response_ = false;
_clear_bit(3);
}
inline bool Message::expect_response() const {
return expect_response_;
}
inline void Message::set_expect_response(bool value) {
_set_bit(3);
expect_response_ = value;
}
// optional int32 ret = 5;
inline bool Message::has_ret() const {
return _has_bit(4);
}
inline void Message::clear_ret() {
ret_ = 0;
_clear_bit(4);
}
inline ::google::protobuf::int32 Message::ret() const {
return ret_;
}
inline void Message::set_ret(::google::protobuf::int32 value) {
_set_bit(4);
ret_ = value;
}
// optional int32 arg0 = 6;
inline bool Message::has_arg0() const {
return _has_bit(5);
}
inline void Message::clear_arg0() {
arg0_ = 0;
_clear_bit(5);
}
inline ::google::protobuf::int32 Message::arg0() const {
return arg0_;
}
inline void Message::set_arg0(::google::protobuf::int32 value) {
_set_bit(5);
arg0_ = value;
}
// optional int32 arg1 = 7;
inline bool Message::has_arg1() const {
return _has_bit(6);
}
inline void Message::clear_arg1() {
arg1_ = 0;
_clear_bit(6);
}
inline ::google::protobuf::int32 Message::arg1() const {
return arg1_;
}
inline void Message::set_arg1(::google::protobuf::int32 value) {
_set_bit(6);
arg1_ = value;
}
// optional int32 arg2 = 8;
inline bool Message::has_arg2() const {
return _has_bit(7);
}
inline void Message::clear_arg2() {
arg2_ = 0;
_clear_bit(7);
}
inline ::google::protobuf::int32 Message::arg2() const {
return arg2_;
}
inline void Message::set_arg2(::google::protobuf::int32 value) {
_set_bit(7);
arg2_ = value;
}
// optional int32 arg3 = 9;
inline bool Message::has_arg3() const {
return _has_bit(8);
}
inline void Message::clear_arg3() {
arg3_ = 0;
_clear_bit(8);
}
inline ::google::protobuf::int32 Message::arg3() const {
return arg3_;
}
inline void Message::set_arg3(::google::protobuf::int32 value) {
_set_bit(8);
arg3_ = value;
}
// optional int32 arg4 = 16;
inline bool Message::has_arg4() const {
return _has_bit(9);
}
inline void Message::clear_arg4() {
arg4_ = 0;
_clear_bit(9);
}
inline ::google::protobuf::int32 Message::arg4() const {
return arg4_;
}
inline void Message::set_arg4(::google::protobuf::int32 value) {
_set_bit(9);
arg4_ = value;
}
// optional int32 arg5 = 17;
inline bool Message::has_arg5() const {
return _has_bit(10);
}
inline void Message::clear_arg5() {
arg5_ = 0;
_clear_bit(10);
}
inline ::google::protobuf::int32 Message::arg5() const {
return arg5_;
}
inline void Message::set_arg5(::google::protobuf::int32 value) {
_set_bit(10);
arg5_ = value;
}
// optional int32 arg6 = 18;
inline bool Message::has_arg6() const {
return _has_bit(11);
}
inline void Message::clear_arg6() {
arg6_ = 0;
_clear_bit(11);
}
inline ::google::protobuf::int32 Message::arg6() const {
return arg6_;
}
inline void Message::set_arg6(::google::protobuf::int32 value) {
_set_bit(11);
arg6_ = value;
}
// optional int32 arg7 = 19;
inline bool Message::has_arg7() const {
return _has_bit(12);
}
inline void Message::clear_arg7() {
arg7_ = 0;
_clear_bit(12);
}
inline ::google::protobuf::int32 Message::arg7() const {
return arg7_;
}
inline void Message::set_arg7(::google::protobuf::int32 value) {
_set_bit(12);
arg7_ = value;
}
// optional int32 arg8 = 20;
inline bool Message::has_arg8() const {
return _has_bit(13);
}
inline void Message::clear_arg8() {
arg8_ = 0;
_clear_bit(13);
}
inline ::google::protobuf::int32 Message::arg8() const {
return arg8_;
}
inline void Message::set_arg8(::google::protobuf::int32 value) {
_set_bit(13);
arg8_ = value;
}
// optional bytes data = 10;
inline bool Message::has_data() const {
return _has_bit(14);
}
inline void Message::clear_data() {
if (data_ != &_default_data_) {
data_->clear();
}
_clear_bit(14);
}
inline const ::std::string& Message::data() const {
return *data_;
}
inline void Message::set_data(const ::std::string& value) {
_set_bit(14);
if (data_ == &_default_data_) {
data_ = new ::std::string;
}
data_->assign(value);
}
inline void Message::set_data(const char* value) {
_set_bit(14);
if (data_ == &_default_data_) {
data_ = new ::std::string;
}
data_->assign(value);
}
inline void Message::set_data(const void* value, size_t size) {
_set_bit(14);
if (data_ == &_default_data_) {
data_ = new ::std::string;
}
data_->assign(reinterpret_cast<const char*>(value), size);
}
inline ::std::string* Message::mutable_data() {
_set_bit(14);
if (data_ == &_default_data_) {
data_ = new ::std::string;
}
return data_;
}
// optional float time = 11;
inline bool Message::has_time() const {
return _has_bit(15);
}
inline void Message::clear_time() {
time_ = 0;
_clear_bit(15);
}
inline float Message::time() const {
return time_;
}
inline void Message::set_time(float value) {
_set_bit(15);
time_ = value;
}
// @@protoc_insertion_point(namespace_scope)
} // namespace GLESv2Debugger
// @@protoc_insertion_point(global_scope)
#endif // PROTOBUF_DebuggerMessage_2eproto__INCLUDED

View File

@ -1,4 +1,3 @@
/*
** Copyright 2011, The Android Open Source Project
**
@ -17,7 +16,8 @@
// auto generated by generate_api_cpp.py
#include "src/header.h"
#include "src/header.h"
#include "src/api.h"
template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
@ -103,6 +103,7 @@ void Debug_glBindBuffer(GLenum target, GLuint buffer)
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glBindBuffer(target, buffer);
getDbgContextThreadSpecific()->glBindBuffer(target, buffer);
return 0;
}
} caller;
@ -321,6 +322,7 @@ void Debug_glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLen
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glBufferData(target, size, data, usage);
getDbgContextThreadSpecific()->glBufferData(target, size, data, usage);
return 0;
}
} caller;
@ -352,6 +354,7 @@ void Debug_glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, cons
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glBufferSubData(target, offset, size, data);
getDbgContextThreadSpecific()->glBufferSubData(target, offset, size, data);
return 0;
}
} caller;
@ -657,6 +660,7 @@ void Debug_glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, G
msg.set_arg6(height);
msg.set_arg7(border);
EXTEND_Debug_glCopyTexImage2D;
int * ret = MessageLoop(caller, msg, expectResponse,
glesv2debugger::Message_Function_glCopyTexImage2D);
}
@ -698,6 +702,7 @@ void Debug_glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint
msg.set_arg6(width);
msg.set_arg7(height);
EXTEND_Debug_glCopyTexSubImage2D;
int * ret = MessageLoop(caller, msg, expectResponse,
glesv2debugger::Message_Function_glCopyTexSubImage2D);
}
@ -773,6 +778,7 @@ void Debug_glDeleteBuffers(GLsizei n, const GLuint* buffers)
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glDeleteBuffers(n, buffers);
getDbgContextThreadSpecific()->glDeleteBuffers(n, buffers);
return 0;
}
} caller;
@ -1018,6 +1024,7 @@ void Debug_glDisableVertexAttribArray(GLuint index)
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glDisableVertexAttribArray(index);
getDbgContextThreadSpecific()->glDisableVertexAttribArray(index);
return 0;
}
} caller;
@ -1058,6 +1065,7 @@ void Debug_glEnableVertexAttribArray(GLuint index)
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glEnableVertexAttribArray(index);
getDbgContextThreadSpecific()->glEnableVertexAttribArray(index);
return 0;
}
} caller;
@ -2468,6 +2476,37 @@ void Debug_glShaderBinary(GLsizei n, const GLuint* shaders, GLenum binaryformat,
glesv2debugger::Message_Function_glShaderBinary);
}
void Debug_glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length)
{
glesv2debugger::Message msg;
const bool expectResponse = false;
struct : public FunctionCall {
GLuint shader;
GLsizei count;
const GLchar** string;
const GLint* length;
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glShaderSource(shader, count, string, length);
return 0;
}
} caller;
caller.shader = shader;
caller.count = count;
caller.string = string;
caller.length = length;
msg.set_arg0(shader);
msg.set_arg1(count);
msg.set_arg2(ToInt(string));
msg.set_arg3(ToInt(length));
// FIXME: check for pointer usage
EXTEND_Debug_glShaderSource;
int * ret = MessageLoop(caller, msg, expectResponse,
glesv2debugger::Message_Function_glShaderSource);
}
void Debug_glStencilFunc(GLenum func, GLint ref, GLuint mask)
{
glesv2debugger::Message msg;
@ -3275,6 +3314,7 @@ void Debug_glUseProgram(GLuint program)
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glUseProgram(program);
getDbgContextThreadSpecific()->glUseProgram(program);
return 0;
}
} caller;
@ -3531,6 +3571,7 @@ void Debug_glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
_c->glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
getDbgContextThreadSpecific()->glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
return 0;
}
} caller;

View File

@ -0,0 +1,31 @@
/*
** Copyright 2011, 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 EXTEND_Debug_glCopyTexImage2D \
void * pixels = malloc(width * height * 4); \
getGLTraceThreadSpecific()->gl.glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); \
msg.set_data(pixels, width * height * 4); \
free(pixels);
#define EXTEND_Debug_glCopyTexSubImage2D EXTEND_Debug_glCopyTexImage2D
#define EXTEND_Debug_glShaderSource \
std::string * const data = msg.mutable_data(); \
for (unsigned i = 0; i < count; i++) \
if (!length || length[i] < 0) \
data->append(string[i]); \
else \
data->append(string[i], length[i]);

View File

@ -0,0 +1,243 @@
/*
** Copyright 2011, 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 "header.h"
namespace android
{
DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
const unsigned MAX_VERTEX_ATTRIBS)
: version(version), hooks(hooks)
, MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
, vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
, hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
{
for (unsigned i = 0; i < MAX_VERTEX_ATTRIBS; i++)
vertexAttribs[i] = VertexAttrib();
}
DbgContext::~DbgContext()
{
delete vertexAttribs;
}
DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks)
{
assert(version < 2);
assert(GL_NO_ERROR == hooks->gl.glGetError());
GLint MAX_VERTEX_ATTRIBS = 0;
hooks->gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &MAX_VERTEX_ATTRIBS);
return new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
}
void DestroyDbgContext(DbgContext * const dbg)
{
delete dbg;
}
void DbgContext::Fetch(const unsigned index, std::string * const data) const
{
// VBO data is already on client, just send user pointer data
for (unsigned i = 0; i < maxAttrib; i++) {
if (!vertexAttribs[i].enabled)
continue;
if (vertexAttribs[i].buffer > 0)
continue;
const char * ptr = (const char *)vertexAttribs[i].ptr;
ptr += index * vertexAttribs[i].stride;
data->append(ptr, vertexAttribs[i].elemSize);
}
}
void DbgContext::glUseProgram(GLuint program)
{
assert(GL_NO_ERROR == hooks->gl.glGetError());
this->program = program;
GLint activeAttributes = 0;
hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
maxAttrib = 0;
GLint maxNameLen = -1;
hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLen);
char * name = new char [maxNameLen + 1];
name[maxNameLen] = 0;
// find total number of attribute slots used
for (unsigned i = 0; i < activeAttributes; i++) {
GLint size = -1;
GLenum type = -1;
hooks->gl.glGetActiveAttrib(program, i, maxNameLen + 1, NULL, &size, &type, name);
GLint slot = hooks->gl.glGetAttribLocation(program, name);
assert(slot >= 0);
switch (type) {
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
slot += size;
break;
case GL_FLOAT_MAT2:
slot += size * 2;
break;
case GL_FLOAT_MAT3:
slot += size * 3;
break;
case GL_FLOAT_MAT4:
slot += size * 4;
break;
default:
assert(0);
}
if (slot > maxAttrib)
maxAttrib = slot;
}
delete name;
}
static bool HasNonVBOAttribs(const DbgContext * const ctx)
{
bool need = false;
for (unsigned i = 0; !need && i < ctx->maxAttrib; i++)
if (ctx->vertexAttribs[i].enabled && ctx->vertexAttribs[i].buffer == 0)
need = true;
return need;
}
void DbgContext::glVertexAttribPointer(GLuint indx, GLint size, GLenum type,
GLboolean normalized, GLsizei stride, const GLvoid* ptr)
{
assert(GL_NO_ERROR == hooks->gl.glGetError());
assert(indx < MAX_VERTEX_ATTRIBS);
vertexAttribs[indx].size = size;
vertexAttribs[indx].type = type;
vertexAttribs[indx].normalized = normalized;
switch (type) {
case GL_FLOAT:
vertexAttribs[indx].elemSize = sizeof(GLfloat) * size;
break;
case GL_INT:
case GL_UNSIGNED_INT:
vertexAttribs[indx].elemSize = sizeof(GLint) * size;
break;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
vertexAttribs[indx].elemSize = sizeof(GLshort) * size;
break;
case GL_BYTE:
case GL_UNSIGNED_BYTE:
vertexAttribs[indx].elemSize = sizeof(GLbyte) * size;
break;
default:
assert(0);
}
if (0 == stride)
stride = vertexAttribs[indx].elemSize;
vertexAttribs[indx].stride = stride;
vertexAttribs[indx].ptr = ptr;
hooks->gl.glGetVertexAttribiv(indx, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
(GLint *)&vertexAttribs[indx].buffer);
hasNonVBOAttribs = HasNonVBOAttribs(this);
}
void DbgContext::glEnableVertexAttribArray(GLuint index)
{
assert(index < MAX_VERTEX_ATTRIBS);
vertexAttribs[index].enabled = true;
hasNonVBOAttribs = HasNonVBOAttribs(this);
}
void DbgContext::glDisableVertexAttribArray(GLuint index)
{
assert(index < MAX_VERTEX_ATTRIBS);
vertexAttribs[index].enabled = false;
hasNonVBOAttribs = HasNonVBOAttribs(this);
}
void DbgContext::glBindBuffer(GLenum target, GLuint buffer)
{
if (GL_ELEMENT_ARRAY_BUFFER != target)
return;
if (0 == buffer) {
indexBuffer = NULL;
return;
}
VBO * b = indexBuffers;
indexBuffer = NULL;
while (b) {
if (b->name == buffer) {
assert(GL_ELEMENT_ARRAY_BUFFER == b->target);
indexBuffer = b;
break;
}
b = b->next;
}
if (!indexBuffer)
indexBuffer = indexBuffers = new VBO(buffer, target, indexBuffers);
}
void DbgContext::glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
{
if (GL_ELEMENT_ARRAY_BUFFER != target)
return;
assert(indexBuffer);
assert(size >= 0);
indexBuffer->size = size;
indexBuffer->data = realloc(indexBuffer->data, size);
memcpy(indexBuffer->data, data, size);
}
void DbgContext::glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
{
if (GL_ELEMENT_ARRAY_BUFFER != target)
return;
assert(indexBuffer);
assert(size >= 0);
assert(offset >= 0);
assert(offset + size <= indexBuffer->size);
memcpy((char *)indexBuffer->data + offset, data, size);
}
void DbgContext::glDeleteBuffers(GLsizei n, const GLuint *buffers)
{
for (unsigned i = 0; i < n; i++) {
for (unsigned j = 0; j < MAX_VERTEX_ATTRIBS; j++)
if (buffers[i] == vertexAttribs[j].buffer) {
vertexAttribs[j].buffer = 0;
vertexAttribs[j].enabled = false;
}
VBO * b = indexBuffers, * previous = NULL;
while (b) {
if (b->name == buffers[i]) {
assert(GL_ELEMENT_ARRAY_BUFFER == b->target);
if (indexBuffer == b)
indexBuffer = NULL;
if (previous)
previous->next = b->next;
else
indexBuffers = b->next;
free(b->data);
delete b;
break;
}
previous = b;
b = b->next;
}
}
hasNonVBOAttribs = HasNonVBOAttribs(this);
}
}; // namespace android

View File

@ -29,8 +29,10 @@
#define EGL_TRACE 1
#include "hooks.h"
#include "glesv2dbg.h"
#define GL_ENTRY(_r, _api, ...) _r Debug_##_api ( __VA_ARGS__ );
#include "../include/glesv2_dbg.h"
#include "glesv2dbg_functions.h"
#include "debugger_message.pb.h"
@ -46,19 +48,72 @@ using namespace com::android;
#define __location__ __FILE__ ":" __HIERALLOC_STRING_2__
#endif
#define ASSERT(expr) if (!(expr)) { LOGD("\n*\n*\n* ASSERT FAILED: %s at %s \n*\n*", #expr, __location__); exit(1); }
#undef assert
#define assert(expr) ASSERT(expr)
#define assert(expr) if (!(expr)) { LOGD("\n*\n*\n* assert: %s at %s \n*\n*", #expr, __location__); int * x = 0; *x = 5; }
//#undef LOGD
//#define LOGD(...)
namespace android
{
struct DbgContext {
const unsigned version; // 0 is GLES1, 1 is GLES2
const gl_hooks_t * const hooks;
const unsigned MAX_VERTEX_ATTRIBS;
struct VertexAttrib {
GLenum type; // element data type
unsigned size; // number of data per element
unsigned stride; // calculated number of bytes between elements
const void * ptr;
unsigned elemSize; // calculated number of bytes per element
GLuint buffer; // buffer name
GLboolean normalized : 1;
GLboolean enabled : 1;
VertexAttrib() : type(0), size(0), stride(0), ptr(NULL), elemSize(0),
buffer(0), normalized(0), enabled(0) {}
} * vertexAttribs;
bool hasNonVBOAttribs; // whether any enabled vertexAttrib is user pointer
struct VBO {
const GLuint name;
const GLenum target;
VBO * next;
void * data; // malloc/free
unsigned size; // in bytes
VBO(const GLuint name, const GLenum target, VBO * head) : name(name),
target(target), next(head), data(NULL), size(0) {}
} * indexBuffers; // linked list of all index buffers
VBO * indexBuffer; // currently bound index buffer
GLuint program;
unsigned maxAttrib; // number of slots used by program
DbgContext(const unsigned version, const gl_hooks_t * const hooks, const unsigned MAX_VERTEX_ATTRIBS);
~DbgContext();
void Fetch(const unsigned index, std::string * const data) const;
void glUseProgram(GLuint program);
void glEnableVertexAttribArray(GLuint index);
void glDisableVertexAttribArray(GLuint index);
void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr);
void glBindBuffer(GLenum target, GLuint buffer);
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
void glDeleteBuffers(GLsizei n, const GLuint *buffers);
};
DbgContext * getDbgContextThreadSpecific();
#define DBGCONTEXT(ctx) DbgContext * const ctx = getDbgContextThreadSpecific();
struct FunctionCall {
virtual const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) = 0;
virtual ~FunctionCall() {}
};
// move these into DbgContext
extern bool capture;
extern int timeMode; // SYSTEM_TIME_

View File

@ -31,8 +31,6 @@ int serverSock = -1, clientSock = -1;
int timeMode = SYSTEM_TIME_THREAD;
void StopDebugServer();
static void Die(const char * msg)
{
LOGD("\n*\n*\n* GLESv2_dbg: Die: %s \n*\n*", msg);
@ -120,7 +118,7 @@ void Receive(glesv2debugger::Message & cmd)
static unsigned bufferSize = 0;
if (bufferSize < len) {
buffer = realloc(buffer, len);
ASSERT(buffer);
assert(buffer);
bufferSize = len;
}
received = recv(clientSock, buffer, len, MSG_WAITALL);
@ -137,8 +135,9 @@ float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd)
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex); // TODO: this is just temporary
if (msg.function() != glesv2debugger::Message_Function_ACK)
assert(msg.has_context_id() && msg.context_id() != 0);
static std::string str;
const_cast<glesv2debugger::Message &>(msg).set_context_id(pthread_self());
msg.SerializeToString(&str);
unsigned len = str.length();
len = htonl(len);
@ -187,10 +186,10 @@ void SetProp(const glesv2debugger::Message & cmd)
int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
const bool expectResponse, const glesv2debugger::Message_Function function)
{
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
DbgContext * const dbg = getDbgContextThreadSpecific();
const int * ret = 0;
glesv2debugger::Message cmd;
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_type(glesv2debugger::Message_Type_BeforeCall);
msg.set_expect_response(expectResponse);
msg.set_function(function);
@ -202,10 +201,10 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
nsecs_t c0 = systemTime(timeMode);
switch (cmd.function()) {
case glesv2debugger::Message_Function_CONTINUE:
ret = functionCall(_c, msg);
ret = functionCall(&dbg->hooks->gl, msg);
if (!msg.has_time()) // some has output data copy, so time inside call
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_function(function);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
@ -220,7 +219,7 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
Receive(cmd);
break;
default:
ASSERT(0); //GenerateCall(msg, cmd);
assert(0); //GenerateCall(msg, cmd);
break;
}
}

View File

@ -1,56 +0,0 @@
/*
** Copyright 2011, 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 "header.h"
void Debug_glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length)
{
glesv2debugger::Message msg;
const bool expectResponse = false;
struct : public FunctionCall {
GLuint shader;
GLsizei count;
const GLchar** string;
const GLint* length;
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
nsecs_t c0 = systemTime(timeMode);
_c->glShaderSource(shader, count, string, length);
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
return 0;
}
} caller;
caller.shader = shader;
caller.count = count;
caller.string = string;
caller.length = length;
msg.set_arg0(shader);
msg.set_arg1(count);
msg.set_arg2(reinterpret_cast<int>(string));
msg.set_arg3(reinterpret_cast<int>(length));
std::string data;
for (unsigned i = 0; i < count; i++)
if (!length || length[i] < 0)
data.append(string[i]);
else
data.append(string[i], length[i]);
msg.set_data(data);
int * ret = MessageLoop(caller, msg, expectResponse,
glesv2debugger::Message_Function_glShaderSource);
}

View File

@ -25,9 +25,9 @@ void * RLEEncode(const void * pixels, const unsigned bytesPerPixel, const unsign
void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
{
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
DbgContext * const dbg = getDbgContextThreadSpecific();
glesv2debugger::Message msg, cmd;
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_type(glesv2debugger::Message_Type_BeforeCall);
const bool expectResponse = false;
msg.set_expect_response(expectResponse);
@ -50,9 +50,9 @@ void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
nsecs_t c0 = systemTime(timeMode);
switch (cmd.function()) {
case glesv2debugger::Message_Function_CONTINUE:
_c->glReadPixels(x, y, width, height, format, type, pixels);
dbg->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels);
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_function(glesv2debugger::Message_Function_glReadPixels);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
@ -68,14 +68,14 @@ void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
msg.clear_data();
msg.set_expect_response(false);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
Send(msg, cmd);
//Send(msg, cmd);
if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_SKIP);
break;
case glesv2debugger::Message_Function_SKIP:
return;
default:
ASSERT(0); //GenerateCall(msg, cmd);
assert(0); //GenerateCall(msg, cmd);
break;
}
}
@ -83,9 +83,9 @@ void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
{
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
DbgContext * const dbg = getDbgContextThreadSpecific();
glesv2debugger::Message msg, cmd;
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_type(glesv2debugger::Message_Type_BeforeCall);
const bool expectResponse = false;
msg.set_expect_response(expectResponse);
@ -93,7 +93,16 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
msg.set_arg0(mode);
msg.set_arg1(first);
msg.set_arg2(count);
void * data = NULL;
msg.set_arg7(dbg->maxAttrib); // indicate capturing vertex data
if (dbg->hasNonVBOAttribs) {
std::string * const data = msg.mutable_data();
for (unsigned i = 0; i < count; i++)
dbg->Fetch(i + first, data);
}
void * pixels = NULL;
GLint readFormat = 0, readType = 0;
int viewport[4] = {};
Send(msg, cmd);
if (!expectResponse)
@ -103,9 +112,9 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
nsecs_t c0 = systemTime(timeMode);
switch (cmd.function()) {
case glesv2debugger::Message_Function_CONTINUE:
_c->glDrawArrays(mode, first, count);
dbg->hooks->gl.glDrawArrays(mode, first, count);
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
@ -118,27 +127,41 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
case glesv2debugger::Message_Function_SKIP:
return;
case glesv2debugger::Message_Function_CAPTURE:
_c->glGetIntegerv(GL_VIEWPORT, viewport);
LOGD("glDrawArrays CAPTURE: glGetIntegerv GL_VIEWPORT x=%d y=%d width=%d height=%d",
viewport[0], viewport[1], viewport[2], viewport[3]);
data = malloc(viewport[2] * viewport[3] * 4);
dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
pixels = malloc(viewport[2] * viewport[3] * 4);
Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
GL_RGBA, GL_UNSIGNED_BYTE, data);
free(data);
readFormat, readType, pixels);
free(pixels);
cmd.set_function(glesv2debugger::Message_Function_SKIP);
break;
default:
ASSERT(0); //GenerateCall(msg, cmd);
assert(0); //GenerateCall(msg, cmd);
break;
}
}
}
template<typename T>
static inline void FetchIndexed(const unsigned count, const T * indices,
std::string * const data, const DbgContext * const ctx)
{
for (unsigned i = 0; i < count; i++) {
if (!ctx->indexBuffer)
data->append((const char *)(indices + i), sizeof(*indices));
if (ctx->hasNonVBOAttribs)
ctx->Fetch(indices[i], data);
}
}
void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
{
gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
DbgContext * const dbg = getDbgContextThreadSpecific();
glesv2debugger::Message msg, cmd;
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_type(glesv2debugger::Message_Type_BeforeCall);
const bool expectResponse = false;
msg.set_expect_response(expectResponse);
@ -147,7 +170,24 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
msg.set_arg1(count);
msg.set_arg2(type);
msg.set_arg3(reinterpret_cast<int>(indices));
void * data = NULL;
msg.set_arg7(dbg->maxAttrib); // indicate capturing vertex data
std::string * const data = msg.mutable_data();
if (GL_UNSIGNED_BYTE == type) {
if (dbg->indexBuffer)
FetchIndexed(count, (unsigned char *)dbg->indexBuffer->data + (unsigned long)indices, data, dbg);
else
FetchIndexed(count, (unsigned char *)indices, data, dbg);
} else if (GL_UNSIGNED_SHORT == type) {
if (dbg->indexBuffer)
FetchIndexed(count, (unsigned short *)((char *)dbg->indexBuffer->data + (unsigned long)indices), data, dbg);
else
FetchIndexed(count, (unsigned short *)indices, data, dbg);
} else
assert(0);
void * pixels = NULL;
GLint readFormat = 0, readType = 0;
int viewport[4] = {};
Send(msg, cmd);
if (!expectResponse)
@ -157,9 +197,9 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
nsecs_t c0 = systemTime(timeMode);
switch (cmd.function()) {
case glesv2debugger::Message_Function_CONTINUE:
_c->glDrawElements(mode, count, type, indices);
dbg->hooks->gl.glDrawElements(mode, count, type, indices);
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
msg.set_context_id(0);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_function(glesv2debugger::Message_Function_glDrawElements);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
@ -172,17 +212,19 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
case glesv2debugger::Message_Function_SKIP:
return;
case glesv2debugger::Message_Function_CAPTURE:
_c->glGetIntegerv(GL_VIEWPORT, viewport);
LOGD("glDrawElements CAPTURE: glGetIntegerv GL_VIEWPORT x=%d y=%d width=%d height=%d",
viewport[0], viewport[1], viewport[2], viewport[3]);
data = malloc(viewport[2] * viewport[3] * 4);
dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
pixels = malloc(viewport[2] * viewport[3] * 4);
Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
GL_RGBA, GL_UNSIGNED_BYTE, data);
free(data);
readFormat, readType, pixels);
free(pixels);
cmd.set_function(glesv2debugger::Message_Function_SKIP);
break;
default:
ASSERT(0); //GenerateCall(msg, cmd);
assert(0); //GenerateCall(msg, cmd);
break;
}
}

32
opengl/libs/glesv2dbg.h Normal file
View File

@ -0,0 +1,32 @@
/*
** Copyright 2011, 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.
*/
#ifndef _GLESV2_DBG_H_
#define _GLESV2_DBG_H_
namespace android
{
struct DbgContext;
DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks);
void DestroyDbgContext(DbgContext * const dbg);
void StartDebugServer(); // create and bind socket if haven't already
void StopDebugServer(); // close socket if open
}; // namespace android
#endif // #ifndef _GLESV2_DBG_H_