diff options
34 files changed, 1879 insertions, 551 deletions
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 340daaf14e..96828c640c 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -127,11 +127,28 @@ public: // be called from the client. status_t setDefaultBufferSize(uint32_t w, uint32_t h); -private: + // getCurrentBuffer returns the buffer associated with the current image. + sp<GraphicBuffer> getCurrentBuffer() const; + + // getCurrentTextureTarget returns the texture target of the current + // texture as returned by updateTexImage(). + GLenum getCurrentTextureTarget() const; + + // getCurrentCrop returns the cropping rectangle of the current buffer + Rect getCurrentCrop() const; + + // getCurrentTransform returns the transform of the current buffer + uint32_t getCurrentTransform() const; + +protected: // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for // all slots. void freeAllBuffers(); + static bool isExternalFormat(uint32_t format); + static GLenum getTextureTarget(uint32_t format); + +private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, @@ -194,6 +211,10 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. int mCurrentTexture; + // mCurrentTextureTarget is the GLES texture target to be used with the + // current texture. + GLenum mCurrentTextureTarget; + // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to properly use @@ -256,7 +277,7 @@ private: // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. - Mutex mMutex; + mutable Mutex mMutex; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index df82bf217f..fe9b04917e 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -27,6 +27,8 @@ namespace android { +class Surface; + class SurfaceTextureClient : public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase> { @@ -36,6 +38,7 @@ public: sp<ISurfaceTexture> getISurfaceTexture() const; private: + friend class Surface; // can't be copied SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs); @@ -78,6 +81,8 @@ private: void freeAllBuffers(); + int getConnectedApi() const; + enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS }; enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS }; enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS }; @@ -121,10 +126,25 @@ private: // a timestamp is auto-generated when queueBuffer is called. int64_t mTimestamp; + // mConnectedApi holds the currently connected API to this surface + int mConnectedApi; + + // mQueryWidth is the width returned by query(). It is set to width + // of the last dequeued buffer or to mReqWidth if no buffer was dequeued. + uint32_t mQueryWidth; + + // mQueryHeight is the height returned by query(). It is set to height + // of the last dequeued buffer or to mReqHeight if no buffer was dequeued. + uint32_t mQueryHeight; + + // mQueryFormat is the format returned by query(). It is set to the last + // dequeued format or to mReqFormat if no buffer was dequeued. + uint32_t mQueryFormat; + // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. - Mutex mMutex; + mutable Mutex mMutex; }; }; // namespace android diff --git a/include/ui/Input.h b/include/ui/Input.h index 0dc29c8e0b..9b92c73c42 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -620,6 +620,11 @@ private: // Oldest sample to consider when calculating the velocity. static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms + // When the total duration of the window of samples being averaged is less + // than the window size, the resulting velocity is scaled to reduce the impact + // of overestimation in short traces. + static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms + // The minimum duration between samples when estimating velocity. static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index e2346f060e..39418f03ad 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -27,6 +27,8 @@ #include <gui/SurfaceTexture.h> +#include <hardware/hardware.h> + #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <surfaceflinger/IGraphicBufferAlloc.h> @@ -82,6 +84,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : mUseDefaultSize(true), mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT), + mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTransform(0), mCurrentTimestamp(0), mLastQueued(INVALID_BUFFER_SLOT), @@ -197,6 +200,7 @@ status_t SurfaceTexture::dequeueBuffer(int *buf) { if (buffer == NULL) { return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; } + if ((mUseDefaultSize) && ((uint32_t(buffer->width) != mDefaultWidth) || (uint32_t(buffer->height) != mDefaultHeight))) { @@ -263,9 +267,6 @@ status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); Mutex::Autolock lock(mMutex); - // We always bind the texture even if we don't update its contents. - glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); - // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, // so this check will fail until a buffer gets queued. if (mCurrentTexture != mLastQueued) { @@ -283,7 +284,15 @@ status_t SurfaceTexture::updateTexImage() { while ((error = glGetError()) != GL_NO_ERROR) { LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); } - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); + + GLenum target = getTextureTarget( + mSlots[mLastQueued].mGraphicBuffer->format); + if (target != mCurrentTextureTarget) { + glDeleteTextures(1, &mTexName); + } + glBindTexture(target, mTexName); + glEGLImageTargetTexture2DOES(target, (GLeglImageOES)image); + bool failed = false; while ((error = glGetError()) != GL_NO_ERROR) { LOGE("error binding external texture image %p (slot %d): %#04x", @@ -296,14 +305,53 @@ status_t SurfaceTexture::updateTexImage() { // Update the SurfaceTexture state. mCurrentTexture = mLastQueued; + mCurrentTextureTarget = target; mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; mCurrentCrop = mLastQueuedCrop; mCurrentTransform = mLastQueuedTransform; mCurrentTimestamp = mLastQueuedTimestamp; + } else { + // We always bind the texture even if we don't update its contents. + glBindTexture(mCurrentTextureTarget, mTexName); } return OK; } +bool SurfaceTexture::isExternalFormat(uint32_t format) +{ + switch (format) { + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return true; + } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + + return false; +} + +GLenum SurfaceTexture::getTextureTarget(uint32_t format) +{ + GLenum target = GL_TEXTURE_2D; +#if defined(GL_OES_EGL_image_external) + if (isExternalFormat(format)) { + target = GL_TEXTURE_EXTERNAL_OES; + } +#endif + return target; +} + +GLenum SurfaceTexture::getCurrentTextureTarget() const { + Mutex::Autolock lock(mMutex); + return mCurrentTextureTarget; +} + void SurfaceTexture::getTransformMatrix(float mtx[16]) { LOGV("SurfaceTexture::getTransformMatrix"); Mutex::Autolock lock(mMutex); @@ -445,6 +493,22 @@ EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, return image; } +sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const { + Mutex::Autolock lock(mMutex); + return mCurrentTextureBuf; +} + +Rect SurfaceTexture::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return mCurrentCrop; +} + +uint32_t SurfaceTexture::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + + static void mtxMul(float out[16], const float a[16], const float b[16]) { out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 29fc4d3edc..f4b24162f0 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -26,8 +26,10 @@ namespace android { SurfaceTextureClient::SurfaceTextureClient( const sp<ISurfaceTexture>& surfaceTexture): mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0), - mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0), - mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() { + mReqHeight(0), mReqFormat(0), mReqUsage(0), + mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0), + mQueryWidth(0), mQueryHeight(0), mQueryFormat(0), + mMutex() { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = setSwapInterval; ANativeWindow::dequeueBuffer = dequeueBuffer; @@ -101,9 +103,10 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { } sp<GraphicBuffer>& gbuf(mSlots[buf]); if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION || - gbuf == 0 || gbuf->getWidth() != mReqWidth || - gbuf->getHeight() != mReqHeight || - uint32_t(gbuf->getPixelFormat()) != mReqFormat || + gbuf == 0 || + (mReqWidth && gbuf->getWidth() != mReqWidth) || + (mReqHeight && gbuf->getHeight() != mReqHeight) || + (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) || (gbuf->getUsage() & mReqUsage) != mReqUsage) { gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight, mReqFormat, mReqUsage); @@ -111,6 +114,9 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); return NO_MEMORY; } + mQueryWidth = gbuf->width; + mQueryHeight = gbuf->height; + mQueryFormat = gbuf->format; } *buffer = gbuf.get(); return OK; @@ -159,13 +165,13 @@ int SurfaceTextureClient::query(int what, int* value) { Mutex::Autolock lock(mMutex); switch (what) { case NATIVE_WINDOW_WIDTH: + *value = mQueryWidth ? mQueryWidth : mReqWidth; + return NO_ERROR; case NATIVE_WINDOW_HEIGHT: - // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't - // override the size? - *value = 0; + *value = mQueryHeight ? mQueryHeight : mReqHeight; return NO_ERROR; case NATIVE_WINDOW_FORMAT: - *value = DEFAULT_FORMAT; + *value = mQueryFormat ? mQueryFormat : mReqFormat; return NO_ERROR; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: *value = MIN_UNDEQUEUED_BUFFERS; @@ -260,16 +266,49 @@ int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) { int SurfaceTextureClient::connect(int api) { LOGV("SurfaceTextureClient::connect"); - // XXX: Implement this! - return INVALID_OPERATION; + Mutex::Autolock lock(mMutex); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + if (mConnectedApi) { + err = -EINVAL; + } else { + mConnectedApi = api; + } + break; + default: + err = -EINVAL; + break; + } + return err; } int SurfaceTextureClient::disconnect(int api) { LOGV("SurfaceTextureClient::disconnect"); - // XXX: Implement this! - return INVALID_OPERATION; + Mutex::Autolock lock(mMutex); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + if (mConnectedApi == api) { + mConnectedApi = 0; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + break; + } + return err; } +int SurfaceTextureClient::getConnectedApi() const +{ + Mutex::Autolock lock(mMutex); + return mConnectedApi; +} + + int SurfaceTextureClient::setUsage(uint32_t reqUsage) { LOGV("SurfaceTextureClient::setUsage"); diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index bbe579ea83..a95f432619 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -832,6 +832,7 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const const Position& oldestPosition = oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)]; nsecs_t lastDuration = 0; + while (numTouches-- > 1) { if (++index == HISTORY_SIZE) { index = 0; @@ -858,6 +859,14 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const // Make sure we used at least one sample. if (samplesUsed != 0) { + // Scale the velocity linearly if the window of samples is small. + nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime; + if (totalDuration < MIN_WINDOW) { + float scale = float(totalDuration) / float(MIN_WINDOW); + accumVx *= scale; + accumVy *= scale; + } + *outVx = accumVx; *outVy = accumVy; return true; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index d5dd126065..b54fb9dd73 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -662,7 +662,8 @@ void Looper::wakeAndLock() { #endif void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) { - sendMessageAtTime(LLONG_MIN, handler, message); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sendMessageAtTime(now, handler, message); } void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index 7d72729e1e..0747efb186 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -13,8 +13,8 @@ LOCAL_SRC_FILES:= \ EGL/hooks.cpp \ EGL/Loader.cpp \ # -LOCAL_STATIC_LIBRARIES += libGLESv2_dbg libprotobuf-cpp-2.3.0-lite liblzf -LOCAL_SHARED_LIBRARIES += libcutils libutils libstlport + +LOCAL_SHARED_LIBRARIES += libcutils libutils libGLESv2_dbg LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libEGL LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 6474c87bbd..9cf722381f 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -46,6 +46,7 @@ #include "egl_impl.h" #include "Loader.h" #include "glesv2dbg.h" +#include "egl_tls.h" #define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r) @@ -58,7 +59,7 @@ namespace android { static char const * const gVendorString = "Android"; static char const * const gVersionString = "1.4 Android META-EGL"; static char const * const gClientApiString = "OpenGL ES"; -static char const * const gExtensionString = +static char const * const gExtensionString = "EGL_KHR_image " "EGL_KHR_image_base " "EGL_KHR_image_pixmap " @@ -221,18 +222,15 @@ struct egl_surface_t : public egl_object_t struct egl_context_t : public egl_object_t { typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref; - + egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, - int impl, egl_connection_t const* cnx, int version) - : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0), - impl(impl), cnx(cnx), version(version), dbg(NULL) + int impl, egl_connection_t const* cnx, int version) + : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0), + impl(impl), cnx(cnx), version(version) { } ~egl_context_t() { - if (dbg) - DestroyDbgContext(dbg); - dbg = NULL; } EGLDisplay dpy; EGLContext context; @@ -242,7 +240,6 @@ 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 @@ -277,15 +274,6 @@ typedef egl_context_t::Ref ContextRef; typedef egl_image_t::Ref ImageRef; typedef egl_sync_t::Ref SyncRef; -struct tls_t -{ - tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { } - EGLint error; - EGLContext ctx; - EGLBoolean logCallWithNoContext; -}; - - // ---------------------------------------------------------------------------- static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; @@ -323,7 +311,7 @@ static void initEglTraceLevel() { int propertyLevel = atoi(value); int applicationLevel = gEGLApplicationTraceLevel; gEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel; - + property_get("debug.egl.debug_proc", value, ""); long pid = getpid(); char procPath[128] = {}; @@ -336,14 +324,20 @@ static void initEglTraceLevel() { { if (!strcmp(value, cmdline)) gEGLDebugLevel = 1; - } + } fclose(file); } - + if (gEGLDebugLevel > 0) { property_get("debug.egl.debug_port", value, "5039"); - StartDebugServer(atoi(value)); + const unsigned short port = (unsigned short)atoi(value); + property_get("debug.egl.debug_forceUseFile", value, "0"); + const bool forceUseFile = (bool)atoi(value); + property_get("debug.egl.debug_maxFileSize", value, "8"); + const unsigned int maxFileSize = atoi(value) << 20; + property_get("debug.egl.debug_filePath", value, "/data/local/tmp/dump.gles2dbg"); + StartDebugServer(port, forceUseFile, maxFileSize, value); } } @@ -586,7 +580,7 @@ static inline NATIVE* egl_to_native_cast(EGL arg) { } static inline -egl_surface_t* get_surface(EGLSurface surface) { +egl_surface_t* get_surface(EGLSurface surface) { return egl_to_native_cast<egl_surface_t>(surface); } @@ -595,11 +589,6 @@ 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); @@ -1442,10 +1431,12 @@ 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); + tls_t * const tls = getTLS(); + if (!tls->dbg && gEGLDebugLevel > 0) + tls->dbg = CreateDbgContext(gEGLThreadLocalStorageKey, c->version, + c->cnx->hooks[c->version]); _c.acquire(); _r.acquire(); _d.acquire(); diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk index fc40799724..9f6e68c4da 100644 --- a/opengl/libs/GLES2_dbg/Android.mk +++ b/opengl/libs/GLES2_dbg/Android.mk @@ -11,7 +11,7 @@ LOCAL_SRC_FILES := \ src/server.cpp \ src/vertex.cpp -LOCAL_C_INCLUDES := \ +LOCAL_C_INCLUDES := \ $(LOCAL_PATH) \ $(LOCAL_PATH)/../ \ external/stlport/stlport \ @@ -21,7 +21,8 @@ LOCAL_C_INCLUDES := \ #LOCAL_CFLAGS += -O0 -g -DDEBUG -UNDEBUG LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI - +LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-2.3.0-lite liblzf +LOCAL_SHARED_LIBRARIES := libcutils libutils libstlport ifeq ($(TARGET_ARCH),arm) LOCAL_CFLAGS += -fstrict-aliasing endif @@ -43,4 +44,6 @@ endif LOCAL_MODULE:= libGLESv2_dbg LOCAL_MODULE_TAGS := optional -include $(BUILD_STATIC_LIBRARY) +include $(BUILD_SHARED_LIBRARY) + +include $(LOCAL_PATH)/test/Android.mk diff --git a/opengl/libs/GLES2_dbg/generate_api_cpp.py b/opengl/libs/GLES2_dbg/generate_api_cpp.py index 66c110f4a1..aeba213d57 100755 --- a/opengl/libs/GLES2_dbg/generate_api_cpp.py +++ b/opengl/libs/GLES2_dbg/generate_api_cpp.py @@ -26,36 +26,36 @@ def RemoveAnnotation(line): return line.replace(annotation, "*") else: return line - + def generate_api(lines): externs = [] i = 0 # these have been hand written - skipFunctions = ["glReadPixels", "glDrawArrays", "glDrawElements"] - + skipFunctions = ["glDrawArrays", "glDrawElements"] + # these have an EXTEND_Debug_* macro for getting data - extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glShaderSource", -"glTexImage2D", "glTexSubImage2D"] - + extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glReadPixels", +"glShaderSource", "glTexImage2D", "glTexSubImage2D"] + # 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(")] functionName = line[line.find("(") + 1: line.find(")")] #extract GL function name parameterList = line[line.find(")(") + 2: line.find(") {")] - + #if line.find("*") >= 0: # extern = "%s Debug_%s(%s);" % (returnType, functionName, parameterList) # externs.append(extern) # continue - + if functionName in skipFunctions: sys.stderr.write("!\n! skipping function '%s'\n!\n" % (functionName)) continue - + parameters = parameterList.split(',') paramIndex = 0 if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")): # unannotated pointer @@ -65,21 +65,21 @@ def generate_api(lines): 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;" - + if parameterList == "void": parameters = [] arguments = "" paramNames = [] inout = "" getData = "" - + callerMembers = "" setCallerMembers = "" setMsgParameters = "" - + for parameter in parameters: const = parameter.find("const") parameter = parameter.replace("const", "") @@ -107,7 +107,7 @@ def generate_api(lines): annotation = "strlen(%s)" % (paramName) else: count = int(annotation) - + setMsgParameters += " msg.set_arg%d(ToInt(%s));\n" % (paramIndex, paramName) if paramType.find("void") >= 0: getData += " msg.mutable_data()->assign(reinterpret_cast<const char *>(%s), %s * sizeof(char));" % (paramName, annotation) @@ -127,7 +127,7 @@ def generate_api(lines): paramIndex += 1 callerMembers += " %s %s;\n" % (paramType, paramName) setCallerMembers += " caller.%s = %s;\n" % (paramName, paramName) - + print " struct : public FunctionCall {" print callerMembers print " const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {" @@ -141,6 +141,11 @@ def generate_api(lines): if inout in ["out", "inout"]: print " msg.set_time((systemTime(timeMode) - c0) * 1e-6f);" print " " + getData + if functionName in extendFunctions: + print "\ +#ifdef EXTEND_AFTER_CALL_Debug_%s\n\ + EXTEND_AFTER_CALL_Debug_%s;\n\ +#endif" % (functionName, functionName) if functionName in contextFunctions: print " getDbgContextThreadSpecific()->%s(%s);" % (functionName, arguments) if returnType == "void": @@ -157,7 +162,10 @@ def generate_api(lines): if inout in ["in", "inout"]: print getData if functionName in extendFunctions: - print " EXTEND_Debug_%s;" % (functionName) + print "\ +#ifdef EXTEND_Debug_%s\n\ + EXTEND_Debug_%s;\n\ +#endif" % (functionName, functionName) print " int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_%s);"\ % (functionName) if returnType != "void": @@ -166,8 +174,8 @@ def generate_api(lines): else: print " return reinterpret_cast<%s>(ret);" % (returnType) print "}\n" - - + + print "// FIXME: the following functions should be written by hand" for extern in externs: print extern @@ -189,18 +197,23 @@ if __name__ == "__main__": ** See the License for the specific language governing permissions and ** limitations under the License. */ - + // auto generated by generate_api_cpp.py +#include <utils/Debug.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; } -""" +template<typename T> static int ToInt(const T & t) +{ + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int)); + return (int &)t; +} +""" lines = open("gl2_api_annotated.in").readlines() generate_api(lines) #lines = open("gl2ext_api.in").readlines() #generate_api(lines) - + diff --git a/opengl/libs/GLES2_dbg/generate_caller_cpp.py b/opengl/libs/GLES2_dbg/generate_caller_cpp.py index eac2292a82..ee4208dd3d 100755 --- a/opengl/libs/GLES2_dbg/generate_caller_cpp.py +++ b/opengl/libs/GLES2_dbg/generate_caller_cpp.py @@ -177,7 +177,6 @@ const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & { LOGD("GenerateCall function=%u", cmd.function()); const int * ret = prevRet; // only some functions have return value - gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; nsecs_t c0 = systemTime(timeMode); switch (cmd.function()) {""") diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py index 466c447cff..57e008cafd 100755 --- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py +++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py @@ -70,41 +70,43 @@ message Message """) i = 0; - + lines = open("gl2_api_annotated.in").readlines() i = generate_gl_entries(output, lines, i) output.write(" // end of GL functions\n") - + #lines = open("gl2ext_api.in").readlines() #i = generate_gl_entries(output, lines, i) #output.write(" // end of GL EXT functions\n") - + lines = open("../EGL/egl_entries.in").readlines() i = generate_egl_entries(output, lines, i) output.write(" // end of GL EXT functions\n") - + output.write(" ACK = %d;\n" % (i)) i += 1 - + output.write(" NEG = %d;\n" % (i)) i += 1 - + output.write(" CONTINUE = %d;\n" % (i)) i += 1 - + output.write(" SKIP = %d;\n" % (i)) i += 1 - + output.write(" SETPROP = %d;\n" % (i)) i += 1 - + output.write(""" } required Function function = 2 [default = NEG]; // type/function of message enum Type { BeforeCall = 0; AfterCall = 1; - Response = 2; // currently used for misc messages + AfterGeneratedCall = 2; + Response = 3; // currently used for misc messages + CompleteCall = 4; // BeforeCall and AfterCall merged } required Type type = 3; required bool expect_response = 4; @@ -125,16 +127,22 @@ message Message ReferencedImage = 0; // for image sourced from ReadPixels NonreferencedImage = 1; // for image sourced from ReadPixels }; - optional DataType data_type = 23; // most data types can be inferred from function - optional int32 pixel_format = 24; // used for image data if format and type - optional int32 pixel_type = 25; // cannot be determined from arg - + // most data types can be inferred from function + optional DataType data_type = 23; + // these are used for image data when they cannot be determined from args + optional int32 pixel_format = 24; + optional int32 pixel_type = 25; + optional int32 image_width = 26; + optional int32 image_height = 27; + optional float time = 11; // duration of previous GL call (ms) enum Prop { - Capture = 0; // arg0 = true | false + CaptureDraw = 0; // arg0 = number of glDrawArrays/Elements to glReadPixels TimeMode = 1; // arg0 = SYSTEM_TIME_* in utils/Timers.h ExpectResponse = 2; // arg0 = enum Function, arg1 = true/false + CaptureSwap = 3; // arg0 = number of eglSwapBuffers to glReadPixels + GLConstant = 4; // arg0 = GLenum, arg1 = constant; send GL impl. constants }; optional Prop prop = 21; // used with SETPROP, value in arg0 optional float clock = 22; // wall clock in seconds @@ -142,6 +150,6 @@ message Message """) output.close() - + os.system("aprotoc --cpp_out=src --java_out=../../../../../development/tools/glesv2debugger/src debugger_message.proto") os.system('mv -f "src/debugger_message.pb.cc" "src/debugger_message.pb.cpp"') diff --git a/opengl/libs/GLES2_dbg/src/api.cpp b/opengl/libs/GLES2_dbg/src/api.cpp index 130ca7e048..c4835478d2 100644 --- a/opengl/libs/GLES2_dbg/src/api.cpp +++ b/opengl/libs/GLES2_dbg/src/api.cpp @@ -16,11 +16,16 @@ // auto generated by generate_api_cpp.py +#include <utils/Debug.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; } +template<typename T> static int ToInt(const T & t) +{ + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int)); + return (int &)t; +} void Debug_glActiveTexture(GLenum texture) { @@ -592,6 +597,9 @@ void Debug_glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, G const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { _c->glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); +#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexImage2D + EXTEND_AFTER_CALL_Debug_glCopyTexImage2D; +#endif return 0; } } caller; @@ -613,7 +621,9 @@ void Debug_glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, G msg.set_arg6(height); msg.set_arg7(border); +#ifdef EXTEND_Debug_glCopyTexImage2D EXTEND_Debug_glCopyTexImage2D; +#endif int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexImage2D); } @@ -632,6 +642,9 @@ void Debug_glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { _c->glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); +#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D + EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D; +#endif return 0; } } caller; @@ -653,7 +666,9 @@ void Debug_glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint msg.set_arg6(width); msg.set_arg7(height); +#ifdef EXTEND_Debug_glCopyTexSubImage2D EXTEND_Debug_glCopyTexSubImage2D; +#endif int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexSubImage2D); } @@ -2164,6 +2179,49 @@ void Debug_glPolygonOffset(GLfloat factor, GLfloat units) int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glPolygonOffset); } +void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) +{ + glesv2debugger::Message msg; + struct : public FunctionCall { + GLint x; + GLint y; + GLsizei width; + GLsizei height; + GLenum format; + GLenum type; + GLvoid* pixels; + + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + _c->glReadPixels(x, y, width, height, format, type, pixels); +#ifdef EXTEND_AFTER_CALL_Debug_glReadPixels + EXTEND_AFTER_CALL_Debug_glReadPixels; +#endif + return 0; + } + } caller; + caller.x = x; + caller.y = y; + caller.width = width; + caller.height = height; + caller.format = format; + caller.type = type; + caller.pixels = pixels; + + msg.set_arg0(x); + msg.set_arg1(y); + msg.set_arg2(width); + msg.set_arg3(height); + msg.set_arg4(format); + msg.set_arg5(type); + msg.set_arg6(ToInt(pixels)); + + // FIXME: check for pointer usage +#ifdef EXTEND_Debug_glReadPixels + EXTEND_Debug_glReadPixels; +#endif + int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glReadPixels); +} + void Debug_glReleaseShaderCompiler(void) { glesv2debugger::Message msg; @@ -2297,6 +2355,9 @@ void Debug_glShaderSource(GLuint shader, GLsizei count, const GLchar** string, c const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { _c->glShaderSource(shader, count, string, length); +#ifdef EXTEND_AFTER_CALL_Debug_glShaderSource + EXTEND_AFTER_CALL_Debug_glShaderSource; +#endif return 0; } } caller; @@ -2311,7 +2372,9 @@ void Debug_glShaderSource(GLuint shader, GLsizei count, const GLchar** string, c msg.set_arg3(ToInt(length)); // FIXME: check for pointer usage +#ifdef EXTEND_Debug_glShaderSource EXTEND_Debug_glShaderSource; +#endif int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glShaderSource); } @@ -2472,6 +2535,9 @@ void Debug_glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsize const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { _c->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); +#ifdef EXTEND_AFTER_CALL_Debug_glTexImage2D + EXTEND_AFTER_CALL_Debug_glTexImage2D; +#endif return 0; } } caller; @@ -2496,7 +2562,9 @@ void Debug_glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsize msg.set_arg8(ToInt(pixels)); // FIXME: check for pointer usage +#ifdef EXTEND_Debug_glTexImage2D EXTEND_Debug_glTexImage2D; +#endif int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexImage2D); } @@ -2616,6 +2684,9 @@ void Debug_glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoff const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { _c->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); +#ifdef EXTEND_AFTER_CALL_Debug_glTexSubImage2D + EXTEND_AFTER_CALL_Debug_glTexSubImage2D; +#endif return 0; } } caller; @@ -2640,7 +2711,9 @@ void Debug_glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoff msg.set_arg8(ToInt(pixels)); // FIXME: check for pointer usage +#ifdef EXTEND_Debug_glTexSubImage2D EXTEND_Debug_glTexSubImage2D; +#endif int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexSubImage2D); } diff --git a/opengl/libs/GLES2_dbg/src/api.h b/opengl/libs/GLES2_dbg/src/api.h index b9fc341e3e..0b227bc66c 100644 --- a/opengl/libs/GLES2_dbg/src/api.h +++ b/opengl/libs/GLES2_dbg/src/api.h @@ -16,19 +16,29 @@ #define EXTEND_Debug_glCopyTexImage2D \ DbgContext * const dbg = getDbgContextThreadSpecific(); \ - GLint readFormat, readType; \ - dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); \ - dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType); \ - unsigned readSize = GetBytesPerPixel(readFormat, readType) * width * height; \ - void * readData = dbg->GetReadPixelsBuffer(readSize); \ - dbg->hooks->gl.glReadPixels(x, y, width, height, readFormat, readType, readData); \ + void * readData = dbg->GetReadPixelsBuffer(4 * width * height); \ + /* pick easy format for client to convert */ \ + dbg->hooks->gl.glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, readData); \ dbg->CompressReadPixelBuffer(msg.mutable_data()); \ msg.set_data_type(msg.ReferencedImage); \ - msg.set_pixel_format(readFormat); \ - msg.set_pixel_type(readType); + msg.set_pixel_format(GL_RGBA); \ + msg.set_pixel_type(GL_UNSIGNED_BYTE); #define EXTEND_Debug_glCopyTexSubImage2D EXTEND_Debug_glCopyTexImage2D +#define EXTEND_AFTER_CALL_Debug_glReadPixels \ + { \ + DbgContext * const dbg = getDbgContextThreadSpecific(); \ + if (dbg->IsReadPixelBuffer(pixels)) { \ + dbg->CompressReadPixelBuffer(msg.mutable_data()); \ + msg.set_data_type(msg.ReferencedImage); \ + } else { \ + const unsigned int size = width * height * GetBytesPerPixel(format, type); \ + dbg->Compress(pixels, size, msg.mutable_data()); \ + msg.set_data_type(msg.NonreferencedImage); \ + } \ + } + #define EXTEND_Debug_glShaderSource \ std::string * const data = msg.mutable_data(); \ for (unsigned i = 0; i < count; i++) \ diff --git a/opengl/libs/GLES2_dbg/src/caller.cpp b/opengl/libs/GLES2_dbg/src/caller.cpp index 9992f05fe6..6b72751bd0 100644 --- a/opengl/libs/GLES2_dbg/src/caller.cpp +++ b/opengl/libs/GLES2_dbg/src/caller.cpp @@ -105,7 +105,6 @@ const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & { LOGD("GenerateCall function=%u", cmd.function()); const int * ret = prevRet; // only some functions have return value - gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; nsecs_t c0 = systemTime(timeMode); switch (cmd.function()) { case glesv2debugger::Message_Function_glActiveTexture: dbg->hooks->gl.glActiveTexture( @@ -772,7 +771,7 @@ const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & msg.set_time((systemTime(timeMode) - c0) * 1e-6f); msg.set_context_id(reinterpret_cast<int>(dbg)); msg.set_function(cmd.function()); - msg.set_type(glesv2debugger::Message_Type_AfterCall); + msg.set_type(glesv2debugger::Message_Type_AfterGeneratedCall); return ret; } diff --git a/opengl/libs/GLES2_dbg/src/caller.h b/opengl/libs/GLES2_dbg/src/caller.h index 54477575aa..e8111b3292 100644 --- a/opengl/libs/GLES2_dbg/src/caller.h +++ b/opengl/libs/GLES2_dbg/src/caller.h @@ -138,7 +138,9 @@ static const int * GenerateCall_glGetProgramiv(DbgContext * const dbg, const glesv2debugger::Message & cmd, glesv2debugger::Message & msg, const int * const prevRet) { - assert(0); + GLint params = -1; + dbg->hooks->gl.glGetProgramiv(cmd.arg0(), cmd.arg1(), ¶ms); + msg.mutable_data()->append(reinterpret_cast<char *>(¶ms), sizeof(params)); return prevRet; } @@ -146,7 +148,10 @@ static const int * GenerateCall_glGetProgramInfoLog(DbgContext * const dbg, const glesv2debugger::Message & cmd, glesv2debugger::Message & msg, const int * const prevRet) { - assert(0); + const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize()); + GLsizei length = -1; + dbg->hooks->gl.glGetProgramInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer()); + msg.mutable_data()->append(dbg->GetBuffer(), length); return prevRet; } @@ -162,7 +167,9 @@ static const int * GenerateCall_glGetShaderiv(DbgContext * const dbg, const glesv2debugger::Message & cmd, glesv2debugger::Message & msg, const int * const prevRet) { - assert(0); + GLint params = -1; + dbg->hooks->gl.glGetShaderiv(cmd.arg0(), cmd.arg1(), ¶ms); + msg.mutable_data()->append(reinterpret_cast<char *>(¶ms), sizeof(params)); return prevRet; } @@ -170,7 +177,10 @@ static const int * GenerateCall_glGetShaderInfoLog(DbgContext * const dbg, const glesv2debugger::Message & cmd, glesv2debugger::Message & msg, const int * const prevRet) { - assert(0); + const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize()); + GLsizei length = -1; + dbg->hooks->gl.glGetShaderInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer()); + msg.mutable_data()->append(dbg->GetBuffer(), length); return prevRet; } diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp index cc7336cf31..7f5b27b393 100644 --- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp +++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp @@ -15,6 +15,7 @@ */ #include "header.h" +#include "egl_tls.h" extern "C" { @@ -24,11 +25,23 @@ extern "C" namespace android { +pthread_key_t dbgEGLThreadLocalStorageKey = -1; + +DbgContext * getDbgContextThreadSpecific() +{ + tls_t* tls = (tls_t*)pthread_getspecific(dbgEGLThreadLocalStorageKey); + return tls->dbg; +} + DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks, - const unsigned MAX_VERTEX_ATTRIBS) + const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat, + const GLenum readType) : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0) , version(version), hooks(hooks) , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS) + , readFormat(readFormat), readType(readType) + , readBytesPerPixel(GetBytesPerPixel(readFormat, readType)) + , captureSwap(0), captureDraw(0) , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS]) , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL) , program(0), maxAttrib(0) @@ -47,13 +60,35 @@ DbgContext::~DbgContext() free(lzf_ref[1]); } -DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks) +DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey, + const unsigned version, const gl_hooks_t * const hooks) { + dbgEGLThreadLocalStorageKey = EGLThreadLocalStorageKey; 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); + GLint readFormat, readType; + hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); + hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType); + DbgContext * const dbg = new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS, readFormat, readType); + + glesv2debugger::Message msg, cmd; + msg.set_context_id(reinterpret_cast<int>(dbg)); + msg.set_expect_response(false); + msg.set_type(msg.Response); + msg.set_function(msg.SETPROP); + msg.set_prop(msg.GLConstant); + msg.set_arg0(GL_MAX_VERTEX_ATTRIBS); + msg.set_arg1(MAX_VERTEX_ATTRIBS); + Send(msg, cmd); + + GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0; + hooks->gl.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &MAX_COMBINED_TEXTURE_IMAGE_UNITS); + msg.set_arg0(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); + msg.set_arg1(MAX_COMBINED_TEXTURE_IMAGE_UNITS); + Send(msg, cmd); + return dbg; } void DestroyDbgContext(DbgContext * const dbg) @@ -113,6 +148,7 @@ void DbgContext::Compress(const void * in_data, unsigned int in_len, { if (!lzf_buf) lzf_buf = (char *)malloc(LZF_CHUNK_SIZE); + assert(lzf_buf); const uint32_t totalDecompSize = in_len; outStr->append((const char *)&totalDecompSize, sizeof(totalDecompSize)); for (unsigned int i = 0; i < in_len; i += LZF_CHUNK_SIZE) { @@ -130,13 +166,46 @@ void DbgContext::Compress(const void * in_data, unsigned int in_len, } } +unsigned char * DbgContext::Decompress(const void * in, const unsigned int inLen, + unsigned int * const outLen) +{ + assert(inLen > 4 * 3); + if (inLen < 4 * 3) + return NULL; + *outLen = *(uint32_t *)in; + unsigned char * const out = (unsigned char *)malloc(*outLen); + unsigned int outPos = 0; + const unsigned char * const end = (const unsigned char *)in + inLen; + for (const unsigned char * inData = (const unsigned char *)in + 4; inData < end; ) { + const uint32_t chunkOut = *(uint32_t *)inData; + inData += 4; + const uint32_t chunkIn = *(uint32_t *)inData; + inData += 4; + if (chunkIn > 0) { + assert(inData + chunkIn <= end); + assert(outPos + chunkOut <= *outLen); + outPos += lzf_decompress(inData, chunkIn, out + outPos, chunkOut); + inData += chunkIn; + } else { + assert(inData + chunkOut <= end); + assert(outPos + chunkOut <= *outLen); + memcpy(out + outPos, inData, chunkOut); + inData += chunkOut; + outPos += chunkOut; + } + } + return out; +} + void * DbgContext::GetReadPixelsBuffer(const unsigned size) { if (lzf_refBufSize < size + 8) { lzf_refBufSize = size + 8; lzf_ref[0] = (unsigned *)realloc(lzf_ref[0], lzf_refBufSize); + assert(lzf_ref[0]); memset(lzf_ref[0], 0, lzf_refBufSize); lzf_ref[1] = (unsigned *)realloc(lzf_ref[1], lzf_refBufSize); + assert(lzf_ref[1]); memset(lzf_ref[1], 0, lzf_refBufSize); } if (lzf_refSize != size) // need to clear unused ref to maintain consistency @@ -151,6 +220,7 @@ void * DbgContext::GetReadPixelsBuffer(const unsigned size) void DbgContext::CompressReadPixelBuffer(std::string * const outStr) { + assert(lzf_ref[0] && lzf_ref[1]); unsigned * const ref = lzf_ref[lzf_readIndex ^ 1]; unsigned * const src = lzf_ref[lzf_readIndex]; for (unsigned i = 0; i < lzf_refSize / sizeof(*ref) + 1; i++) @@ -158,13 +228,34 @@ void DbgContext::CompressReadPixelBuffer(std::string * const outStr) Compress(ref, lzf_refSize, outStr); } +char * DbgContext::GetBuffer() +{ + if (!lzf_buf) + lzf_buf = (char *)malloc(LZF_CHUNK_SIZE); + assert(lzf_buf); + return lzf_buf; +} + +unsigned int DbgContext::GetBufferSize() +{ + if (!lzf_buf) + lzf_buf = (char *)malloc(LZF_CHUNK_SIZE); + assert(lzf_buf); + if (lzf_buf) + return LZF_CHUNK_SIZE; + else + return 0; +} + void DbgContext::glUseProgram(GLuint program) { while (GLenum error = hooks->gl.glGetError()) - LOGD("DbgContext::glUseProgram: before glGetError() = 0x%.4X", error); - + LOGD("DbgContext::glUseProgram(%u): before glGetError() = 0x%.4X", + program, error); this->program = program; - + maxAttrib = 0; + if (program == 0) + return; GLint activeAttributes = 0; hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes); maxAttrib = 0; @@ -202,9 +293,9 @@ void DbgContext::glUseProgram(GLuint program) maxAttrib = slot; } delete name; - while (GLenum error = hooks->gl.glGetError()) - LOGD("DbgContext::glUseProgram: after glGetError() = 0x%.4X", error); + LOGD("DbgContext::glUseProgram(%u): after glGetError() = 0x%.4X", + program, error); } static bool HasNonVBOAttribs(const DbgContext * const ctx) @@ -254,14 +345,16 @@ void DbgContext::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, void DbgContext::glEnableVertexAttribArray(GLuint index) { - assert(index < MAX_VERTEX_ATTRIBS); + if (index >= MAX_VERTEX_ATTRIBS) + return; vertexAttribs[index].enabled = true; hasNonVBOAttribs = HasNonVBOAttribs(this); } void DbgContext::glDisableVertexAttribArray(GLuint index) { - assert(index < MAX_VERTEX_ATTRIBS); + if (index >= MAX_VERTEX_ATTRIBS) + return; vertexAttribs[index].enabled = false; hasNonVBOAttribs = HasNonVBOAttribs(this); } diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp index 046c954000..50f70f7dff 100644 --- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp +++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp @@ -436,6 +436,8 @@ bool Message_Type_IsValid(int value) { case 0: case 1: case 2: + case 3: + case 4: return true; default: return false; @@ -445,7 +447,9 @@ bool Message_Type_IsValid(int value) { #ifndef _MSC_VER const Message_Type Message::BeforeCall; const Message_Type Message::AfterCall; +const Message_Type Message::AfterGeneratedCall; const Message_Type Message::Response; +const Message_Type Message::CompleteCall; const Message_Type Message::Type_MIN; const Message_Type Message::Type_MAX; const int Message::Type_ARRAYSIZE; @@ -472,6 +476,8 @@ bool Message_Prop_IsValid(int value) { case 0: case 1: case 2: + case 3: + case 4: return true; default: return false; @@ -479,9 +485,11 @@ bool Message_Prop_IsValid(int value) { } #ifndef _MSC_VER -const Message_Prop Message::Capture; +const Message_Prop Message::CaptureDraw; const Message_Prop Message::TimeMode; const Message_Prop Message::ExpectResponse; +const Message_Prop Message::CaptureSwap; +const Message_Prop Message::GLConstant; const Message_Prop Message::Prop_MIN; const Message_Prop Message::Prop_MAX; const int Message::Prop_ARRAYSIZE; @@ -506,6 +514,8 @@ const int Message::kDataFieldNumber; const int Message::kDataTypeFieldNumber; const int Message::kPixelFormatFieldNumber; const int Message::kPixelTypeFieldNumber; +const int Message::kImageWidthFieldNumber; +const int Message::kImageHeightFieldNumber; const int Message::kTimeFieldNumber; const int Message::kPropFieldNumber; const int Message::kClockFieldNumber; @@ -545,6 +555,8 @@ void Message::SharedCtor() { data_type_ = 0; pixel_format_ = 0; pixel_type_ = 0; + image_width_ = 0; + image_height_ = 0; time_ = 0; prop_ = 0; clock_ = 0; @@ -606,6 +618,8 @@ void Message::Clear() { if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) { pixel_format_ = 0; pixel_type_ = 0; + image_width_ = 0; + image_height_ = 0; time_ = 0; prop_ = 0; clock_ = 0; @@ -790,7 +804,7 @@ bool Message::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( input, &time_))); - _set_bit(18); + _set_bit(20); } else { goto handle_uninterpreted; } @@ -905,7 +919,7 @@ bool Message::MergePartialFromCodedStream( DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( input, &clock_))); - _set_bit(20); + _set_bit(22); } else { goto handle_uninterpreted; } @@ -960,6 +974,38 @@ bool Message::MergePartialFromCodedStream( } else { goto handle_uninterpreted; } + if (input->ExpectTag(208)) goto parse_image_width; + break; + } + + // optional int32 image_width = 26; + case 26: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_image_width: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &image_width_))); + _set_bit(18); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(216)) goto parse_image_height; + break; + } + + // optional int32 image_height = 27; + case 27: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_image_height: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &image_height_))); + _set_bit(19); + } else { + goto handle_uninterpreted; + } if (input->ExpectAtEnd()) return true; break; } @@ -1035,7 +1081,7 @@ void Message::SerializeWithCachedSizes( } // optional float time = 11; - if (_has_bit(18)) { + if (_has_bit(20)) { ::google::protobuf::internal::WireFormatLite::WriteFloat(11, this->time(), output); } @@ -1065,13 +1111,13 @@ void Message::SerializeWithCachedSizes( } // optional .com.android.glesv2debugger.Message.Prop prop = 21; - if (_has_bit(19)) { + if (_has_bit(21)) { ::google::protobuf::internal::WireFormatLite::WriteEnum( 21, this->prop(), output); } // optional float clock = 22; - if (_has_bit(20)) { + if (_has_bit(22)) { ::google::protobuf::internal::WireFormatLite::WriteFloat(22, this->clock(), output); } @@ -1091,6 +1137,16 @@ void Message::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteInt32(25, this->pixel_type(), output); } + // optional int32 image_width = 26; + if (_has_bit(18)) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(26, this->image_width(), output); + } + + // optional int32 image_height = 27; + if (_has_bit(19)) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(27, this->image_height(), output); + } + } int Message::ByteSize() const { @@ -1222,6 +1278,20 @@ int Message::ByteSize() const { this->pixel_type()); } + // optional int32 image_width = 26; + if (has_image_width()) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->image_width()); + } + + // optional int32 image_height = 27; + if (has_image_height()) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->image_height()); + } + // optional float time = 11; if (has_time()) { total_size += 1 + 4; @@ -1312,12 +1382,18 @@ void Message::MergeFrom(const Message& from) { set_pixel_type(from.pixel_type()); } if (from._has_bit(18)) { - set_time(from.time()); + set_image_width(from.image_width()); } if (from._has_bit(19)) { - set_prop(from.prop()); + set_image_height(from.image_height()); } if (from._has_bit(20)) { + set_time(from.time()); + } + if (from._has_bit(21)) { + set_prop(from.prop()); + } + if (from._has_bit(22)) { set_clock(from.clock()); } } @@ -1355,6 +1431,8 @@ void Message::Swap(Message* other) { std::swap(data_type_, other->data_type_); std::swap(pixel_format_, other->pixel_format_); std::swap(pixel_type_, other->pixel_type_); + std::swap(image_width_, other->image_width_); + std::swap(image_height_, other->image_height_); std::swap(time_, other->time_); std::swap(prop_, other->prop_); std::swap(clock_, other->clock_); diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h index b2ec5a0ac3..5c946644a1 100644 --- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h +++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h @@ -236,11 +236,13 @@ const int Message_Function_Function_ARRAYSIZE = Message_Function_Function_MAX + enum Message_Type { Message_Type_BeforeCall = 0, Message_Type_AfterCall = 1, - Message_Type_Response = 2 + Message_Type_AfterGeneratedCall = 2, + Message_Type_Response = 3, + Message_Type_CompleteCall = 4 }; bool Message_Type_IsValid(int value); const Message_Type Message_Type_Type_MIN = Message_Type_BeforeCall; -const Message_Type Message_Type_Type_MAX = Message_Type_Response; +const Message_Type Message_Type_Type_MAX = Message_Type_CompleteCall; const int Message_Type_Type_ARRAYSIZE = Message_Type_Type_MAX + 1; enum Message_DataType { @@ -253,13 +255,15 @@ const Message_DataType Message_DataType_DataType_MAX = Message_DataType_Nonrefer const int Message_DataType_DataType_ARRAYSIZE = Message_DataType_DataType_MAX + 1; enum Message_Prop { - Message_Prop_Capture = 0, + Message_Prop_CaptureDraw = 0, Message_Prop_TimeMode = 1, - Message_Prop_ExpectResponse = 2 + Message_Prop_ExpectResponse = 2, + Message_Prop_CaptureSwap = 3, + Message_Prop_GLConstant = 4 }; bool Message_Prop_IsValid(int value); -const Message_Prop Message_Prop_Prop_MIN = Message_Prop_Capture; -const Message_Prop Message_Prop_Prop_MAX = Message_Prop_ExpectResponse; +const Message_Prop Message_Prop_Prop_MIN = Message_Prop_CaptureDraw; +const Message_Prop Message_Prop_Prop_MAX = Message_Prop_GLConstant; const int Message_Prop_Prop_ARRAYSIZE = Message_Prop_Prop_MAX + 1; // =================================================================== @@ -510,7 +514,9 @@ class Message : public ::google::protobuf::MessageLite { typedef Message_Type Type; static const Type BeforeCall = Message_Type_BeforeCall; static const Type AfterCall = Message_Type_AfterCall; + static const Type AfterGeneratedCall = Message_Type_AfterGeneratedCall; static const Type Response = Message_Type_Response; + static const Type CompleteCall = Message_Type_CompleteCall; static inline bool Type_IsValid(int value) { return Message_Type_IsValid(value); } @@ -535,9 +541,11 @@ class Message : public ::google::protobuf::MessageLite { Message_DataType_DataType_ARRAYSIZE; typedef Message_Prop Prop; - static const Prop Capture = Message_Prop_Capture; + static const Prop CaptureDraw = Message_Prop_CaptureDraw; static const Prop TimeMode = Message_Prop_TimeMode; static const Prop ExpectResponse = Message_Prop_ExpectResponse; + static const Prop CaptureSwap = Message_Prop_CaptureSwap; + static const Prop GLConstant = Message_Prop_GLConstant; static inline bool Prop_IsValid(int value) { return Message_Prop_IsValid(value); } @@ -679,6 +687,20 @@ class Message : public ::google::protobuf::MessageLite { inline ::google::protobuf::int32 pixel_type() const; inline void set_pixel_type(::google::protobuf::int32 value); + // optional int32 image_width = 26; + inline bool has_image_width() const; + inline void clear_image_width(); + static const int kImageWidthFieldNumber = 26; + inline ::google::protobuf::int32 image_width() const; + inline void set_image_width(::google::protobuf::int32 value); + + // optional int32 image_height = 27; + inline bool has_image_height() const; + inline void clear_image_height(); + static const int kImageHeightFieldNumber = 27; + inline ::google::protobuf::int32 image_height() const; + inline void set_image_height(::google::protobuf::int32 value); + // optional float time = 11; inline bool has_time() const; inline void clear_time(); @@ -723,6 +745,8 @@ class Message : public ::google::protobuf::MessageLite { int data_type_; ::google::protobuf::int32 pixel_format_; ::google::protobuf::int32 pixel_type_; + ::google::protobuf::int32 image_width_; + ::google::protobuf::int32 image_height_; float time_; int prop_; float clock_; @@ -730,7 +754,7 @@ class Message : public ::google::protobuf::MessageLite { friend void protobuf_AssignDesc_debugger_5fmessage_2eproto(); friend void protobuf_ShutdownFile_debugger_5fmessage_2eproto(); - ::google::protobuf::uint32 _has_bits_[(21 + 31) / 32]; + ::google::protobuf::uint32 _has_bits_[(23 + 31) / 32]; // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? inline bool _has_bit(int index) const { @@ -1070,52 +1094,84 @@ inline void Message::set_pixel_type(::google::protobuf::int32 value) { pixel_type_ = value; } +// optional int32 image_width = 26; +inline bool Message::has_image_width() const { + return _has_bit(18); +} +inline void Message::clear_image_width() { + image_width_ = 0; + _clear_bit(18); +} +inline ::google::protobuf::int32 Message::image_width() const { + return image_width_; +} +inline void Message::set_image_width(::google::protobuf::int32 value) { + _set_bit(18); + image_width_ = value; +} + +// optional int32 image_height = 27; +inline bool Message::has_image_height() const { + return _has_bit(19); +} +inline void Message::clear_image_height() { + image_height_ = 0; + _clear_bit(19); +} +inline ::google::protobuf::int32 Message::image_height() const { + return image_height_; +} +inline void Message::set_image_height(::google::protobuf::int32 value) { + _set_bit(19); + image_height_ = value; +} + // optional float time = 11; inline bool Message::has_time() const { - return _has_bit(18); + return _has_bit(20); } inline void Message::clear_time() { time_ = 0; - _clear_bit(18); + _clear_bit(20); } inline float Message::time() const { return time_; } inline void Message::set_time(float value) { - _set_bit(18); + _set_bit(20); time_ = value; } // optional .com.android.glesv2debugger.Message.Prop prop = 21; inline bool Message::has_prop() const { - return _has_bit(19); + return _has_bit(21); } inline void Message::clear_prop() { prop_ = 0; - _clear_bit(19); + _clear_bit(21); } inline ::com::android::glesv2debugger::Message_Prop Message::prop() const { return static_cast< ::com::android::glesv2debugger::Message_Prop >(prop_); } inline void Message::set_prop(::com::android::glesv2debugger::Message_Prop value) { GOOGLE_DCHECK(::com::android::glesv2debugger::Message_Prop_IsValid(value)); - _set_bit(19); + _set_bit(21); prop_ = value; } // optional float clock = 22; inline bool Message::has_clock() const { - return _has_bit(20); + return _has_bit(22); } inline void Message::clear_clock() { clock_ = 0; - _clear_bit(20); + _clear_bit(22); } inline float Message::clock() const { return clock_; } inline void Message::set_clock(float value) { - _set_bit(20); + _set_bit(22); clock_ = value; } diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp index 3a20e21ba2..eb28d06076 100644 --- a/opengl/libs/GLES2_dbg/src/egl.cpp +++ b/opengl/libs/GLES2_dbg/src/egl.cpp @@ -18,6 +18,7 @@ EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) { + DbgContext * const dbg = getDbgContextThreadSpecific(); glesv2debugger::Message msg; struct : public FunctionCall { EGLDisplay dpy; @@ -33,7 +34,21 @@ EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) msg.set_arg0(reinterpret_cast<int>(dpy)); msg.set_arg1(reinterpret_cast<int>(draw)); - + if (dbg->captureSwap > 0) { + dbg->captureSwap--; + int viewport[4] = {}; + dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport); + void * pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] * + dbg->readBytesPerPixel); + dbg->hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2], + viewport[3], dbg->readFormat, dbg->readType, pixels); + dbg->CompressReadPixelBuffer(msg.mutable_data()); + msg.set_data_type(msg.ReferencedImage); + msg.set_pixel_format(dbg->readFormat); + msg.set_pixel_type(dbg->readType); + msg.set_image_width(viewport[2]); + msg.set_image_height(viewport[3]); + } int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_eglSwapBuffers); return static_cast<EGLBoolean>(reinterpret_cast<int>(ret)); } diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h index 9218da5b31..f2b1fa6635 100644 --- a/opengl/libs/GLES2_dbg/src/header.h +++ b/opengl/libs/GLES2_dbg/src/header.h @@ -14,6 +14,9 @@ ** limitations under the License. */ +#ifndef ANDROID_GLES2_DBG_HEADER_H +#define ANDROID_GLES2_DBG_HEADER_H + #include <stdlib.h> #include <ctype.h> #include <string.h> @@ -24,9 +27,7 @@ #include <cutils/log.h> #include <utils/Timers.h> -#include <../../../libcore/include/StaticAssert.h> -#define EGL_TRACE 1 #include "hooks.h" #include "glesv2dbg.h" @@ -39,8 +40,6 @@ using namespace android; using namespace com::android; -#define API_ENTRY(_api) Debug_##_api - #ifndef __location__ #define __HIERALLOC_STRING_0__(s) #s #define __HIERALLOC_STRING_1__(s) __HIERALLOC_STRING_0__(s) @@ -74,9 +73,10 @@ struct GLFunctionBitfield { }; struct DbgContext { -private: static const unsigned int LZF_CHUNK_SIZE = 256 * 1024; - char * lzf_buf; // malloc / free; for lzf chunk compression + +private: + char * lzf_buf; // malloc / free; for lzf chunk compression and other uses // used as buffer and reference frame for ReadPixels; malloc/free unsigned * lzf_ref [2]; @@ -84,9 +84,14 @@ private: unsigned lzf_refSize, lzf_refBufSize; // bytes public: - const unsigned version; // 0 is GLES1, 1 is GLES2 + const unsigned int version; // 0 is GLES1, 1 is GLES2 const gl_hooks_t * const hooks; - const unsigned MAX_VERTEX_ATTRIBS; + const unsigned int MAX_VERTEX_ATTRIBS; + const GLenum readFormat, readType; // implementation supported glReadPixels + const unsigned int readBytesPerPixel; + + unsigned int captureSwap; // number of eglSwapBuffers to glReadPixels + unsigned int captureDraw; // number of glDrawArrays/Elements to glReadPixels GLFunctionBitfield expectResponse; @@ -119,16 +124,21 @@ public: unsigned maxAttrib; // number of slots used by program DbgContext(const unsigned version, const gl_hooks_t * const hooks, - const unsigned MAX_VERTEX_ATTRIBS); + const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat, + const GLenum readType); ~DbgContext(); void Fetch(const unsigned index, std::string * const data) const; void Compress(const void * in_data, unsigned in_len, std::string * const outStr); + static unsigned char * Decompress(const void * in, const unsigned int inLen, + unsigned int * const outLen); // malloc/free void * GetReadPixelsBuffer(const unsigned size); bool IsReadPixelBuffer(const void * const ptr) { return ptr == lzf_ref[lzf_readIndex]; } void CompressReadPixelBuffer(std::string * const outStr); + char * GetBuffer(); // allocates lzf_buf if NULL + unsigned int GetBufferSize(); // allocates lzf_buf if NULL void glUseProgram(GLuint program); void glEnableVertexAttribArray(GLuint index); @@ -141,9 +151,7 @@ public: 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, @@ -152,7 +160,6 @@ struct FunctionCall { }; // move these into DbgContext as static -extern bool capture; extern int timeMode; // SYSTEM_TIME_ extern int clientSock, serverSock; @@ -169,3 +176,5 @@ void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd); const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & cmd, glesv2debugger::Message & msg, const int * const prevRet); }; // namespace android { + +#endif // #ifndef ANDROID_GLES2_DBG_HEADER_H diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp index 7039c84850..0c711bf200 100644 --- a/opengl/libs/GLES2_dbg/src/server.cpp +++ b/opengl/libs/GLES2_dbg/src/server.cpp @@ -28,7 +28,8 @@ namespace android { int serverSock = -1, clientSock = -1; - +FILE * file = NULL; +unsigned int MAX_FILE_SIZE = 0; int timeMode = SYSTEM_TIME_THREAD; static void Die(const char * msg) @@ -38,18 +39,25 @@ static void Die(const char * msg) exit(1); } -void StartDebugServer(unsigned short port) +void StartDebugServer(const unsigned short port, const bool forceUseFile, + const unsigned int maxFileSize, const char * const filePath) { + MAX_FILE_SIZE = maxFileSize; + LOGD("GLESv2_dbg: StartDebugServer"); - if (serverSock >= 0) + if (serverSock >= 0 || file) return; LOGD("GLESv2_dbg: StartDebugServer create socket"); struct sockaddr_in server = {}, client = {}; /* Create the TCP socket */ - if ((serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { - Die("Failed to create socket"); + if (forceUseFile || (serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + file = fopen(filePath, "wb"); + if (!file) + Die("Failed to create socket and file"); + else + return; } /* Construct the server sockaddr_in structure */ server.sin_family = AF_INET; /* Internet/IP */ @@ -92,13 +100,17 @@ void StopDebugServer() close(serverSock); serverSock = -1; } - + if (file) { + fclose(file); + file = NULL; + } } void Receive(glesv2debugger::Message & cmd) { + if (clientSock < 0) + return; unsigned len = 0; - int received = recv(clientSock, &len, 4, MSG_WAITALL); if (received < 0) Die("Failed to receive response length"); @@ -106,7 +118,6 @@ void Receive(glesv2debugger::Message & cmd) LOGD("received %dB: %.8X", received, len); Die("Received length mismatch, expected 4"); } - len = ntohl(len); static void * buffer = NULL; static unsigned bufferSize = 0; if (bufferSize < len) { @@ -125,6 +136,8 @@ void Receive(glesv2debugger::Message & cmd) bool TryReceive(glesv2debugger::Message & cmd) { + if (clientSock < 0) + return false; fd_set readSet; FD_ZERO(&readSet); FD_SET(clientSock, &readSet); @@ -146,14 +159,34 @@ bool TryReceive(glesv2debugger::Message & cmd) float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd) { + // TODO: use per DbgContext send/receive buffer and async socket + // instead of mutex and blocking io; watch out for large messages static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock(&mutex); // TODO: this is just temporary + struct Autolock { + Autolock() { + pthread_mutex_lock(&mutex); + } + ~Autolock() { + pthread_mutex_unlock(&mutex); + } + } autolock; if (msg.function() != glesv2debugger::Message_Function_ACK) assert(msg.has_context_id() && msg.context_id() != 0); static std::string str; msg.SerializeToString(&str); - uint32_t len = htonl(str.length()); + const uint32_t len = str.length(); + if (clientSock < 0) { + if (file) { + fwrite(&len, sizeof(len), 1, file); + fwrite(str.data(), len, 1, file); + if (ftell(file) >= MAX_FILE_SIZE) { + fclose(file); + Die("MAX_FILE_SIZE reached"); + } + } + return 0; + } int sent = -1; sent = send(clientSock, &len, sizeof(len), 0); if (sent != sizeof(len)) { @@ -161,36 +194,37 @@ float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd) Die("Failed to send message length"); } nsecs_t c0 = systemTime(timeMode); - sent = send(clientSock, str.c_str(), str.length(), 0); + sent = send(clientSock, str.data(), str.length(), 0); float t = (float)ns2ms(systemTime(timeMode) - c0); if (sent != str.length()) { LOGD("actual sent=%d expected=%d clientSock=%d", sent, str.length(), clientSock); Die("Failed to send message"); } - + // TODO: factor Receive & TryReceive out and into MessageLoop, or add control argument. + // mean while, if server is sending a SETPROP then don't try to receive, + // because server will not be processing received command + if (msg.function() == msg.SETPROP) + return t; // try to receive commands even though not expecting response, - // since client can send SETPROP commands anytime + // since client can send SETPROP and other commands anytime if (!msg.expect_response()) { if (TryReceive(cmd)) { - LOGD("Send: TryReceived"); if (glesv2debugger::Message_Function_SETPROP == cmd.function()) - LOGD("Send: received SETPROP"); + LOGD("Send: TryReceived SETPROP"); else - LOGD("Send: received something else"); + LOGD("Send: TryReceived %u", cmd.function()); } } else Receive(cmd); - - pthread_mutex_unlock(&mutex); return t; } void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd) { switch (cmd.prop()) { - case glesv2debugger::Message_Prop_Capture: - LOGD("SetProp Message_Prop_Capture %d", cmd.arg0()); - capture = cmd.arg0(); + case glesv2debugger::Message_Prop_CaptureDraw: + LOGD("SetProp Message_Prop_CaptureDraw %d", cmd.arg0()); + dbg->captureDraw = cmd.arg0(); break; case glesv2debugger::Message_Prop_TimeMode: LOGD("SetProp Message_Prop_TimeMode %d", cmd.arg0()); @@ -200,6 +234,10 @@ void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd) LOGD("SetProp Message_Prop_ExpectResponse %d=%d", cmd.arg0(), cmd.arg1()); dbg->expectResponse.Bit((glesv2debugger::Message_Function)cmd.arg0(), cmd.arg1()); break; + case glesv2debugger::Message_Prop_CaptureSwap: + LOGD("SetProp CaptureSwap %d", cmd.arg0()); + dbg->captureSwap = cmd.arg0(); + break; default: assert(0); } @@ -213,12 +251,17 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg, glesv2debugger::Message cmd; msg.set_context_id(reinterpret_cast<int>(dbg)); msg.set_type(glesv2debugger::Message_Type_BeforeCall); - const bool expectResponse = dbg->expectResponse.Bit(function); + bool expectResponse = dbg->expectResponse.Bit(function); msg.set_expect_response(expectResponse); msg.set_function(function); - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + + // when not exectResponse, set cmd to CONTINUE then SKIP + // cmd will be overwritten by received command + cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_expect_response(expectResponse); + glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); while (true) { msg.Clear(); nsecs_t c0 = systemTime(timeMode); @@ -233,22 +276,34 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg, msg.set_function(function); msg.set_type(glesv2debugger::Message_Type_AfterCall); msg.set_expect_response(expectResponse); - if (!expectResponse) + if (!expectResponse) { cmd.set_function(glesv2debugger::Message_Function_SKIP); + cmd.set_expect_response(false); + } + oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); break; case glesv2debugger::Message_Function_SKIP: return const_cast<int *>(ret); case glesv2debugger::Message_Function_SETPROP: SetProp(dbg, cmd); - Receive(cmd); + expectResponse = cmd.expect_response(); + if (!expectResponse) // SETPROP is "out of band" + cmd.set_function(oldCmd); + else + Receive(cmd); break; default: ret = GenerateCall(dbg, cmd, msg, ret); msg.set_expect_response(expectResponse); - if (!expectResponse) + if (!expectResponse) { cmd.set_function(cmd.SKIP); + cmd.set_expect_response(expectResponse); + } + oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); break; } } diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp index 471e5adf91..029ee3bf50 100644 --- a/opengl/libs/GLES2_dbg/src/vertex.cpp +++ b/opengl/libs/GLES2_dbg/src/vertex.cpp @@ -21,74 +21,13 @@ namespace android bool capture; // capture after each glDraw* } -void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) -{ - DbgContext * const dbg = getDbgContextThreadSpecific(); - glesv2debugger::Message msg, cmd; - msg.set_context_id(reinterpret_cast<int>(dbg)); - msg.set_type(glesv2debugger::Message_Type_BeforeCall); - const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glReadPixels); - msg.set_expect_response(expectResponse); - msg.set_function(glesv2debugger::Message_Function_glReadPixels); - msg.set_arg0(x); - msg.set_arg1(y); - msg.set_arg2(width); - msg.set_arg3(height); - msg.set_arg4(format); - msg.set_arg5(type); - msg.set_arg6(reinterpret_cast<int>(pixels)); - - const unsigned size = width * height * GetBytesPerPixel(format, type); - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_CONTINUE); - Send(msg, cmd); - float t = 0; - while (true) { - msg.Clear(); - nsecs_t c0 = systemTime(timeMode); - switch (cmd.function()) { - case glesv2debugger::Message_Function_CONTINUE: - dbg->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels); - msg.set_time((systemTime(timeMode) - c0) * 1e-6f); - 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); - if (dbg->IsReadPixelBuffer(pixels)) { - dbg->CompressReadPixelBuffer(msg.mutable_data()); - msg.set_data_type(msg.ReferencedImage); - } else { - dbg->Compress(pixels, size, msg.mutable_data()); - msg.set_data_type(msg.NonreferencedImage); - } - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_SKIP); - Send(msg, cmd); - break; - case glesv2debugger::Message_Function_SKIP: - return; - case glesv2debugger::Message_Function_SETPROP: - SetProp(dbg, cmd); - Receive(cmd); - break; - default: - GenerateCall(dbg, cmd, msg, NULL); - msg.set_expect_response(expectResponse); - if (!expectResponse) - cmd.set_function(cmd.SKIP); - Send(msg, cmd); - break; - } - } -} - void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count) { DbgContext * const dbg = getDbgContextThreadSpecific(); glesv2debugger::Message msg, cmd; msg.set_context_id(reinterpret_cast<int>(dbg)); msg.set_type(glesv2debugger::Message_Type_BeforeCall); - const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays); + bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays); msg.set_expect_response(expectResponse); msg.set_function(glesv2debugger::Message_Function_glDrawArrays); msg.set_arg0(mode); @@ -103,11 +42,12 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count) } void * pixels = NULL; - GLint readFormat = 0, readType = 0; int viewport[4] = {}; - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_expect_response(expectResponse); + glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); while (true) { msg.Clear(); nsecs_t c0 = systemTime(timeMode); @@ -119,33 +59,47 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count) msg.set_function(glesv2debugger::Message_Function_glDrawArrays); msg.set_type(glesv2debugger::Message_Type_AfterCall); msg.set_expect_response(expectResponse); - if (!expectResponse) + if (!expectResponse) { cmd.set_function(glesv2debugger::Message_Function_SKIP); + cmd.set_expect_response(false); + } + oldCmd = cmd.function(); Send(msg, cmd); - if (capture) { + expectResponse = cmd.expect_response(); + // TODO: pack glReadPixels data with vertex data instead of + // relying on sperate call for transport, this would allow + // auto generated message loop using EXTEND_Debug macro + if (dbg->captureDraw > 0) { + dbg->captureDraw--; 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 = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] * - GetBytesPerPixel(readFormat, readType)); + dbg->readBytesPerPixel); Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], - readFormat, readType, pixels); + dbg->readFormat, dbg->readType, pixels); } break; case glesv2debugger::Message_Function_SKIP: return; case glesv2debugger::Message_Function_SETPROP: SetProp(dbg, cmd); - Receive(cmd); + expectResponse = cmd.expect_response(); + if (!expectResponse) // SETPROP is "out of band" + cmd.set_function(oldCmd); + else + Receive(cmd); break; default: GenerateCall(dbg, cmd, msg, NULL); msg.set_expect_response(expectResponse); - if (!expectResponse) + if (!expectResponse) { cmd.set_function(cmd.SKIP); + cmd.set_expect_response(expectResponse); + } + oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); break; } } @@ -169,7 +123,7 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* glesv2debugger::Message msg, cmd; msg.set_context_id(reinterpret_cast<int>(dbg)); msg.set_type(glesv2debugger::Message_Type_BeforeCall); - const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements); + bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements); msg.set_expect_response(expectResponse); msg.set_function(glesv2debugger::Message_Function_glDrawElements); msg.set_arg0(mode); @@ -195,11 +149,12 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* assert(0); void * pixels = NULL; - GLint readFormat = 0, readType = 0; int viewport[4] = {}; - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_expect_response(expectResponse); + glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); while (true) { msg.Clear(); nsecs_t c0 = systemTime(timeMode); @@ -211,33 +166,45 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* msg.set_function(glesv2debugger::Message_Function_glDrawElements); msg.set_type(glesv2debugger::Message_Type_AfterCall); msg.set_expect_response(expectResponse); - if (!expectResponse) + if (!expectResponse) { cmd.set_function(glesv2debugger::Message_Function_SKIP); + cmd.set_expect_response(false); + } + oldCmd = cmd.function(); Send(msg, cmd); - if (capture) { + expectResponse = cmd.expect_response(); + // TODO: pack glReadPixels data with vertex data instead of + // relying on sperate call for transport, this would allow + // auto generated message loop using EXTEND_Debug macro + if (dbg->captureDraw > 0) { + dbg->captureDraw--; 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 = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] * - GetBytesPerPixel(readFormat, readType)); + dbg->readBytesPerPixel); Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], - readFormat, readType, pixels); + dbg->readFormat, dbg->readType, pixels); } break; case glesv2debugger::Message_Function_SKIP: return; case glesv2debugger::Message_Function_SETPROP: SetProp(dbg, cmd); - Receive(cmd); + expectResponse = cmd.expect_response(); + if (!expectResponse) // SETPROP is "out of band" + cmd.set_function(oldCmd); + else + Receive(cmd); break; default: GenerateCall(dbg, cmd, msg, NULL); msg.set_expect_response(expectResponse); - if (!expectResponse) + if (!expectResponse) { cmd.set_function(cmd.SKIP); + cmd.set_expect_response(expectResponse); + } + oldCmd = cmd.function(); Send(msg, cmd); + expectResponse = cmd.expect_response(); break; } } diff --git a/opengl/libs/GLES2_dbg/test/Android.mk b/opengl/libs/GLES2_dbg/test/Android.mk new file mode 100644 index 0000000000..14a84b447f --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../../ \ + external/gtest/include \ + external/stlport/stlport \ + external/protobuf/src \ + bionic \ + external \ +# + +LOCAL_SRC_FILES:= \ + test_main.cpp \ + test_server.cpp \ + test_socket.cpp \ +# + +LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2_dbg libstlport +LOCAL_STATIC_LIBRARIES := libgtest libprotobuf-cpp-2.3.0-lite liblzf +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE:= libGLESv2_dbg_test + +ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER +endif +ifneq ($(TARGET_SIMULATOR),true) + LOCAL_C_INCLUDES += bionic/libc/private +endif + +LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\" +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS += -fvisibility=hidden + +include $(BUILD_EXECUTABLE) + diff --git a/opengl/libs/GLES2_dbg/test/test_main.cpp b/opengl/libs/GLES2_dbg/test/test_main.cpp new file mode 100644 index 0000000000..058bea4d6f --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/test_main.cpp @@ -0,0 +1,234 @@ +/* + ** 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" +#include "gtest/gtest.h" +#include "hooks.h" + +namespace +{ + +// The fixture for testing class Foo. +class DbgContextTest : public ::testing::Test +{ +protected: + android::DbgContext dbg; + gl_hooks_t hooks; + + DbgContextTest() + : dbg(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE) { + // You can do set-up work for each test here. + hooks.gl.glGetError = GetError; + } + + static GLenum GetError() { + return GL_NO_ERROR; + } + + virtual ~DbgContextTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } +}; + +TEST_F(DbgContextTest, GetReadPixelBuffer) +{ + const unsigned int bufferSize = 512; + // test that it's allocating two buffers and swapping them + void * const buffer0 = dbg.GetReadPixelsBuffer(bufferSize); + ASSERT_NE((void *)NULL, buffer0); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) { + EXPECT_EQ(0, ((unsigned int *)buffer0)[i]) + << "GetReadPixelsBuffer should allocate and zero"; + ((unsigned int *)buffer0)[i] = i * 13; + } + + void * const buffer1 = dbg.GetReadPixelsBuffer(bufferSize); + ASSERT_NE((void *)NULL, buffer1); + EXPECT_NE(buffer0, buffer1); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) { + EXPECT_EQ(0, ((unsigned int *)buffer1)[i]) + << "GetReadPixelsBuffer should allocate and zero"; + ((unsigned int *)buffer1)[i] = i * 17; + } + + void * const buffer2 = dbg.GetReadPixelsBuffer(bufferSize); + EXPECT_EQ(buffer2, buffer0); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) + EXPECT_EQ(i * 13, ((unsigned int *)buffer2)[i]) + << "GetReadPixelsBuffer should swap buffers"; + + void * const buffer3 = dbg.GetReadPixelsBuffer(bufferSize); + EXPECT_EQ(buffer3, buffer1); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) + EXPECT_EQ(i * 17, ((unsigned int *)buffer3)[i]) + << "GetReadPixelsBuffer should swap buffers"; + + void * const buffer4 = dbg.GetReadPixelsBuffer(bufferSize); + EXPECT_NE(buffer3, buffer4); + EXPECT_EQ(buffer0, buffer2); + EXPECT_EQ(buffer1, buffer3); + EXPECT_EQ(buffer2, buffer4); + + // it reallocs as necessary; 0 size may result in NULL + for (unsigned int i = 0; i < 42; i++) { + void * const buffer = dbg.GetReadPixelsBuffer(((i & 7)) << 20); + EXPECT_NE((void *)NULL, buffer) + << "should be able to get a variety of reasonable sizes"; + EXPECT_TRUE(dbg.IsReadPixelBuffer(buffer)); + } +} + +TEST_F(DbgContextTest, CompressReadPixelBuffer) +{ + const unsigned int bufferSize = dbg.LZF_CHUNK_SIZE * 4 + 33; + std::string out; + unsigned char * buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + buffer[i] = i * 13; + dbg.CompressReadPixelBuffer(&out); + uint32_t decompSize = 0; + ASSERT_LT(12, out.length()); // at least written chunk header + ASSERT_EQ(bufferSize, *(uint32_t *)out.data()) + << "total decompressed size should be as requested in GetReadPixelsBuffer"; + for (unsigned int i = 4; i < out.length();) { + const uint32_t outSize = *(uint32_t *)(out.data() + i); + i += 4; + const uint32_t inSize = *(uint32_t *)(out.data() + i); + i += 4; + if (inSize == 0) + i += outSize; // chunk not compressed + else + i += inSize; // skip the actual compressed chunk + decompSize += outSize; + } + ASSERT_EQ(bufferSize, decompSize); + decompSize = 0; + + unsigned char * decomp = dbg.Decompress(out.data(), out.length(), &decompSize); + ASSERT_EQ(decompSize, bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + EXPECT_EQ((unsigned char)(i * 13), decomp[i]) << "xor with 0 ref is identity"; + free(decomp); + + buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + buffer[i] = i * 13; + out.clear(); + dbg.CompressReadPixelBuffer(&out); + decompSize = 0; + decomp = dbg.Decompress(out.data(), out.length(), &decompSize); + ASSERT_EQ(decompSize, bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + EXPECT_EQ(0, decomp[i]) << "xor with same ref is 0"; + free(decomp); + + buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + buffer[i] = i * 19; + out.clear(); + dbg.CompressReadPixelBuffer(&out); + decompSize = 0; + decomp = dbg.Decompress(out.data(), out.length(), &decompSize); + ASSERT_EQ(decompSize, bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + EXPECT_EQ((unsigned char)(i * 13) ^ (unsigned char)(i * 19), decomp[i]) + << "xor ref"; + free(decomp); +} + +TEST_F(DbgContextTest, UseProgram) +{ + static const GLuint _program = 74568; + static const struct Attribute { + const char * name; + GLint location; + GLint size; + GLenum type; + } _attributes [] = { + {"aaa", 2, 2, GL_FLOAT_VEC2}, + {"bb", 6, 2, GL_FLOAT_MAT2}, + {"c", 1, 1, GL_FLOAT}, + }; + static const unsigned int _attributeCount = sizeof(_attributes) / sizeof(*_attributes); + struct GL { + static void GetProgramiv(GLuint program, GLenum pname, GLint* params) { + EXPECT_EQ(_program, program); + ASSERT_NE((GLint *)NULL, params); + switch (pname) { + case GL_ACTIVE_ATTRIBUTES: + *params = _attributeCount; + return; + case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: + *params = 4; // includes NULL terminator + return; + default: + ADD_FAILURE() << "not handled pname: " << pname; + } + } + + static GLint GetAttribLocation(GLuint program, const GLchar* name) { + EXPECT_EQ(_program, program); + for (unsigned int i = 0; i < _attributeCount; i++) + if (!strcmp(name, _attributes[i].name)) + return _attributes[i].location; + ADD_FAILURE() << "unknown attribute name: " << name; + return -1; + } + + static void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, + GLsizei* length, GLint* size, GLenum* type, GLchar* name) { + EXPECT_EQ(_program, program); + ASSERT_LT(index, _attributeCount); + const Attribute & att = _attributes[index]; + ASSERT_GE(bufsize, strlen(att.name) + 1); + ASSERT_NE((GLint *)NULL, size); + ASSERT_NE((GLenum *)NULL, type); + ASSERT_NE((GLchar *)NULL, name); + strcpy(name, att.name); + if (length) + *length = strlen(name) + 1; + *size = att.size; + *type = att.type; + } + }; + hooks.gl.glGetProgramiv = GL::GetProgramiv; + hooks.gl.glGetAttribLocation = GL::GetAttribLocation; + hooks.gl.glGetActiveAttrib = GL::GetActiveAttrib; + dbg.glUseProgram(_program); + EXPECT_EQ(10, dbg.maxAttrib); + dbg.glUseProgram(0); + EXPECT_EQ(0, dbg.maxAttrib); +} +} // namespace + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/opengl/libs/GLES2_dbg/test/test_server.cpp b/opengl/libs/GLES2_dbg/test/test_server.cpp new file mode 100644 index 0000000000..b6401e0c79 --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/test_server.cpp @@ -0,0 +1,251 @@ +/* + ** 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" +#include "gtest/gtest.h" +#include "egl_tls.h" +#include "hooks.h" + +namespace android +{ +extern FILE * file; +extern unsigned int MAX_FILE_SIZE; +extern pthread_key_t dbgEGLThreadLocalStorageKey; +}; + +// tmpfile fails, so need to manually make a writable file first +static const char * filePath = "/data/local/tmp/dump.gles2dbg"; + +class ServerFileTest : public ::testing::Test +{ +protected: + ServerFileTest() { } + + virtual ~ServerFileTest() { } + + virtual void SetUp() { + MAX_FILE_SIZE = 8 << 20; + ASSERT_EQ((FILE *)NULL, file); + file = fopen("/data/local/tmp/dump.gles2dbg", "wb+"); + ASSERT_NE((FILE *)NULL, file) << "make sure file is writable: " + << filePath; + } + + virtual void TearDown() { + ASSERT_NE((FILE *)NULL, file); + fclose(file); + file = NULL; + } + + void Read(glesv2debugger::Message & msg) const { + msg.Clear(); + uint32_t len = 0; + ASSERT_EQ(sizeof(len), fread(&len, 1, sizeof(len), file)); + ASSERT_GT(len, 0u); + char * buffer = new char [len]; + ASSERT_EQ(len, fread(buffer, 1, len, file)); + msg.ParseFromArray(buffer, len); + delete buffer; + } + + void CheckNoAvailable() { + const long pos = ftell(file); + fseek(file, 0, SEEK_END); + EXPECT_EQ(pos, ftell(file)) << "check no available"; + } +}; + +TEST_F(ServerFileTest, Send) +{ + glesv2debugger::Message msg, cmd, read; + msg.set_context_id(1); + msg.set_function(msg.glFinish); + msg.set_expect_response(false); + msg.set_type(msg.BeforeCall); + rewind(file); + android::Send(msg, cmd); + rewind(file); + Read(read); + EXPECT_EQ(msg.context_id(), read.context_id()); + EXPECT_EQ(msg.function(), read.function()); + EXPECT_EQ(msg.expect_response(), read.expect_response()); + EXPECT_EQ(msg.type(), read.type()); +} + +TEST_F(ServerFileTest, CreateDbgContext) +{ + gl_hooks_t hooks; + struct Constant { + GLenum pname; + GLint param; + }; + static const Constant constants [] = { + {GL_MAX_VERTEX_ATTRIBS, 16}, + {GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 32}, + {GL_IMPLEMENTATION_COLOR_READ_FORMAT, GL_RGBA}, + {GL_IMPLEMENTATION_COLOR_READ_TYPE, GL_UNSIGNED_BYTE}, + }; + struct HookMock { + static void GetIntegerv(GLenum pname, GLint* params) { + ASSERT_TRUE(params != NULL); + for (unsigned int i = 0; i < sizeof(constants) / sizeof(*constants); i++) + if (pname == constants[i].pname) { + *params = constants[i].param; + return; + } + FAIL() << "GetIntegerv unknown pname: " << pname; + } + static GLenum GetError() { + return GL_NO_ERROR; + } + }; + hooks.gl.glGetError = HookMock::GetError; + hooks.gl.glGetIntegerv = HookMock::GetIntegerv; + DbgContext * const dbg = CreateDbgContext(-1, 1, &hooks); + ASSERT_TRUE(dbg != NULL); + EXPECT_TRUE(dbg->vertexAttribs != NULL); + + rewind(file); + glesv2debugger::Message read; + for (unsigned int i = 0; i < 2; i++) { + Read(read); + EXPECT_EQ(reinterpret_cast<int>(dbg), read.context_id()); + EXPECT_FALSE(read.expect_response()); + EXPECT_EQ(read.Response, read.type()); + EXPECT_EQ(read.SETPROP, read.function()); + EXPECT_EQ(read.GLConstant, read.prop()); + GLint expectedConstant = 0; + HookMock::GetIntegerv(read.arg0(), &expectedConstant); + EXPECT_EQ(expectedConstant, read.arg1()); + } + CheckNoAvailable(); + DestroyDbgContext(dbg); +} + +void * glNoop() +{ + return 0; +} + +class ServerFileContextTest : public ServerFileTest +{ +protected: + tls_t tls; + gl_hooks_t hooks; + + ServerFileContextTest() { } + + virtual ~ServerFileContextTest() { } + + virtual void SetUp() { + ServerFileTest::SetUp(); + + if (dbgEGLThreadLocalStorageKey == -1) + pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL); + ASSERT_NE(-1, dbgEGLThreadLocalStorageKey); + tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE); + ASSERT_NE((void *)NULL, tls.dbg); + pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls); + for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++) + ((void **)&hooks)[i] = reinterpret_cast<void *>(glNoop); + } + + virtual void TearDown() { + ServerFileTest::TearDown(); + } +}; + +TEST_F(ServerFileContextTest, MessageLoop) +{ + static const int arg0 = 45; + static const float arg7 = -87.2331f; + static const int arg8 = -3; + static const int * ret = reinterpret_cast<int *>(870); + + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + msg.set_arg0(arg0); + msg.set_arg7((int &)arg7); + msg.set_arg8(arg8); + return ret; + } + } caller; + const int contextId = reinterpret_cast<int>(tls.dbg); + glesv2debugger::Message msg, read; + + EXPECT_EQ(ret, MessageLoop(caller, msg, msg.glFinish)); + + rewind(file); + Read(read); + EXPECT_EQ(contextId, read.context_id()); + EXPECT_EQ(read.glFinish, read.function()); + EXPECT_EQ(false, read.expect_response()); + EXPECT_EQ(read.BeforeCall, read.type()); + + Read(read); + EXPECT_EQ(contextId, read.context_id()); + EXPECT_EQ(read.glFinish, read.function()); + EXPECT_EQ(false, read.expect_response()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_TRUE(read.has_time()); + EXPECT_EQ(arg0, read.arg0()); + const int readArg7 = read.arg7(); + EXPECT_EQ(arg7, (float &)readArg7); + EXPECT_EQ(arg8, read.arg8()); + + const long pos = ftell(file); + fseek(file, 0, SEEK_END); + EXPECT_EQ(pos, ftell(file)) + << "should only write the BeforeCall and AfterCall messages"; +} + +TEST_F(ServerFileContextTest, DisableEnableVertexAttribArray) +{ + Debug_glEnableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 2); // should just ignore invalid index + + glesv2debugger::Message read; + rewind(file); + Read(read); + EXPECT_EQ(read.glEnableVertexAttribArray, read.function()); + EXPECT_EQ(tls.dbg->MAX_VERTEX_ATTRIBS + 2, read.arg0()); + Read(read); + + rewind(file); + Debug_glDisableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 4); // should just ignore invalid index + rewind(file); + Read(read); + Read(read); + + for (unsigned int i = 0; i < tls.dbg->MAX_VERTEX_ATTRIBS; i += 5) { + rewind(file); + Debug_glEnableVertexAttribArray(i); + EXPECT_TRUE(tls.dbg->vertexAttribs[i].enabled); + rewind(file); + Read(read); + EXPECT_EQ(read.glEnableVertexAttribArray, read.function()); + EXPECT_EQ(i, read.arg0()); + Read(read); + + rewind(file); + Debug_glDisableVertexAttribArray(i); + EXPECT_FALSE(tls.dbg->vertexAttribs[i].enabled); + rewind(file); + Read(read); + EXPECT_EQ(read.glDisableVertexAttribArray, read.function()); + EXPECT_EQ(i, read.arg0()); + Read(read); + } +} diff --git a/opengl/libs/GLES2_dbg/test/test_socket.cpp b/opengl/libs/GLES2_dbg/test/test_socket.cpp new file mode 100644 index 0000000000..617292e280 --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/test_socket.cpp @@ -0,0 +1,474 @@ +/* + ** 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 <sys/socket.h> +#include <sys/ioctl.h> + +#include "header.h" +#include "gtest/gtest.h" +#include "egl_tls.h" +#include "hooks.h" + +namespace android +{ +extern int serverSock, clientSock; +extern pthread_key_t dbgEGLThreadLocalStorageKey; +}; + +void * glNoop(); + +class SocketContextTest : public ::testing::Test +{ +protected: + tls_t tls; + gl_hooks_t hooks; + int sock; + char * buffer; + unsigned int bufferSize; + + SocketContextTest() : sock(-1) { + } + + virtual ~SocketContextTest() { + } + + virtual void SetUp() { + if (dbgEGLThreadLocalStorageKey == -1) + pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL); + ASSERT_NE(-1, dbgEGLThreadLocalStorageKey); + tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE); + ASSERT_TRUE(tls.dbg != NULL); + pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls); + for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++) + ((void **)&hooks)[i] = (void *)glNoop; + + int socks[2] = {-1, -1}; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socks)); + clientSock = socks[0]; + sock = socks[1]; + + bufferSize = 128; + buffer = new char [128]; + ASSERT_NE((char *)NULL, buffer); + } + + virtual void TearDown() { + close(sock); + close(clientSock); + clientSock = -1; + delete buffer; + } + + void Write(glesv2debugger::Message & msg) const { + msg.set_context_id((int)tls.dbg); + msg.set_type(msg.Response); + ASSERT_TRUE(msg.has_context_id()); + ASSERT_TRUE(msg.has_function()); + ASSERT_TRUE(msg.has_type()); + ASSERT_TRUE(msg.has_expect_response()); + static std::string str; + msg.SerializeToString(&str); + const uint32_t len = str.length(); + ASSERT_EQ(sizeof(len), send(sock, &len, sizeof(len), 0)); + ASSERT_EQ(str.length(), send(sock, str.data(), str.length(), 0)); + } + + void Read(glesv2debugger::Message & msg) { + int available = 0; + ASSERT_EQ(0, ioctl(sock, FIONREAD, &available)); + ASSERT_GT(available, 0); + uint32_t len = 0; + ASSERT_EQ(sizeof(len), recv(sock, &len, sizeof(len), 0)); + if (len > bufferSize) { + bufferSize = len; + buffer = new char[bufferSize]; + ASSERT_TRUE(buffer != NULL); + } + ASSERT_EQ(len, recv(sock, buffer, len, 0)); + msg.Clear(); + msg.ParseFromArray(buffer, len); + ASSERT_TRUE(msg.has_context_id()); + ASSERT_TRUE(msg.has_function()); + ASSERT_TRUE(msg.has_type()); + ASSERT_TRUE(msg.has_expect_response()); + } + + void CheckNoAvailable() { + int available = 0; + ASSERT_EQ(0, ioctl(sock, FIONREAD, &available)); + ASSERT_EQ(available, 0); + } +}; + +TEST_F(SocketContextTest, MessageLoopSkip) +{ + static const int arg0 = 45; + static const float arg7 = -87.2331f; + static const int arg8 = -3; + static const int * ret = (int *)870; + + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + msg.set_arg0(arg0); + msg.set_arg7((int &)arg7); + msg.set_arg8(arg8); + return ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + tls.dbg->expectResponse.Bit(msg.glFinish, true); + + cmd.set_function(cmd.SKIP); + cmd.set_expect_response(false); + Write(cmd); + + EXPECT_NE(ret, MessageLoop(caller, msg, msg.glFinish)); + + Read(read); + EXPECT_EQ(read.glFinish, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_NE(arg0, read.arg0()); + EXPECT_NE((int &)arg7, read.arg7()); + EXPECT_NE(arg8, read.arg8()); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, MessageLoopContinue) +{ + static const int arg0 = GL_FRAGMENT_SHADER; + static const int ret = -342; + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + msg.set_ret(ret); + return (int *)ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + tls.dbg->expectResponse.Bit(msg.glCreateShader, true); + + cmd.set_function(cmd.CONTINUE); + cmd.set_expect_response(false); // MessageLoop should automatically skip after continue + Write(cmd); + + msg.set_arg0(arg0); + EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateShader)); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_EQ(arg0, read.arg0()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_EQ(ret, read.ret()); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, MessageLoopGenerateCall) +{ + static const int ret = -342; + static unsigned int createShader, createProgram; + createShader = 0; + createProgram = 0; + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + const int r = (int)_c->glCreateProgram(); + msg.set_ret(r); + return (int *)r; + } + static GLuint CreateShader(const GLenum type) { + createShader++; + return type; + } + static GLuint CreateProgram() { + createProgram++; + return ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glCreateShader = caller.CreateShader; + hooks.gl.glCreateProgram = caller.CreateProgram; + tls.dbg->expectResponse.Bit(msg.glCreateProgram, true); + + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_FRAGMENT_SHADER); + cmd.set_expect_response(true); + Write(cmd); + + cmd.Clear(); + cmd.set_function(cmd.CONTINUE); + cmd.set_expect_response(true); + Write(cmd); + + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_VERTEX_SHADER); + cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards + Write(cmd); + + EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram)); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_EQ(ret, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_VERTEX_SHADER, read.ret()); + + EXPECT_EQ(2, createShader); + EXPECT_EQ(1, createProgram); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, MessageLoopSetProp) +{ + static const int ret = -342; + static unsigned int createShader, createProgram; + createShader = 0; + createProgram = 0; + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + const int r = (int)_c->glCreateProgram(); + msg.set_ret(r); + return (int *)r; + } + static GLuint CreateShader(const GLenum type) { + createShader++; + return type; + } + static GLuint CreateProgram() { + createProgram++; + return ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glCreateShader = caller.CreateShader; + hooks.gl.glCreateProgram = caller.CreateProgram; + tls.dbg->expectResponse.Bit(msg.glCreateProgram, false); + + cmd.set_function(cmd.SETPROP); + cmd.set_prop(cmd.ExpectResponse); + cmd.set_arg0(cmd.glCreateProgram); + cmd.set_arg1(true); + cmd.set_expect_response(true); + Write(cmd); + + cmd.Clear(); + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_FRAGMENT_SHADER); + cmd.set_expect_response(true); + Write(cmd); + + cmd.set_function(cmd.SETPROP); + cmd.set_prop(cmd.CaptureDraw); + cmd.set_arg0(819); + cmd.set_expect_response(true); + Write(cmd); + + cmd.Clear(); + cmd.set_function(cmd.CONTINUE); + cmd.set_expect_response(true); + Write(cmd); + + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_VERTEX_SHADER); + cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards + Write(cmd); + + EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram)); + + EXPECT_TRUE(tls.dbg->expectResponse.Bit(msg.glCreateProgram)); + EXPECT_EQ(819, tls.dbg->captureDraw); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_EQ(ret, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_VERTEX_SHADER, read.ret()); + + EXPECT_EQ(2, createShader); + EXPECT_EQ(1, createProgram); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, TexImage2D) +{ + static const GLenum _target = GL_TEXTURE_2D; + static const GLint _level = 1, _internalformat = GL_RGBA; + static const GLsizei _width = 2, _height = 2; + static const GLint _border = 333; + static const GLenum _format = GL_RGB, _type = GL_UNSIGNED_SHORT_5_6_5; + static const short _pixels [_width * _height] = {11, 22, 33, 44}; + static unsigned int texImage2D; + texImage2D = 0; + + struct Caller { + static void TexImage2D(GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid* pixels) { + EXPECT_EQ(_target, target); + EXPECT_EQ(_level, level); + EXPECT_EQ(_internalformat, internalformat); + EXPECT_EQ(_width, width); + EXPECT_EQ(_height, height); + EXPECT_EQ(_border, border); + EXPECT_EQ(_format, format); + EXPECT_EQ(_type, type); + EXPECT_EQ(0, memcmp(_pixels, pixels, sizeof(_pixels))); + texImage2D++; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glTexImage2D = caller.TexImage2D; + tls.dbg->expectResponse.Bit(msg.glTexImage2D, false); + + Debug_glTexImage2D(_target, _level, _internalformat, _width, _height, _border, + _format, _type, _pixels); + EXPECT_EQ(1, texImage2D); + + Read(read); + EXPECT_EQ(read.glTexImage2D, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_EQ(_target, read.arg0()); + EXPECT_EQ(_level, read.arg1()); + EXPECT_EQ(_internalformat, read.arg2()); + EXPECT_EQ(_width, read.arg3()); + EXPECT_EQ(_height, read.arg4()); + EXPECT_EQ(_border, read.arg5()); + EXPECT_EQ(_format, read.arg6()); + EXPECT_EQ(_type, read.arg7()); + + EXPECT_TRUE(read.has_data()); + uint32_t dataLen = 0; + const unsigned char * data = tls.dbg->Decompress(read.data().data(), + read.data().length(), &dataLen); + EXPECT_EQ(sizeof(_pixels), dataLen); + if (sizeof(_pixels) == dataLen) + EXPECT_EQ(0, memcmp(_pixels, data, sizeof(_pixels))); + + Read(read); + EXPECT_EQ(read.glTexImage2D, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, CopyTexImage2D) +{ + static const GLenum _target = GL_TEXTURE_2D; + static const GLint _level = 1, _internalformat = GL_RGBA; + static const GLint _x = 9, _y = 99; + static const GLsizei _width = 2, _height = 3; + static const GLint _border = 333; + static const int _pixels [_width * _height] = {11, 22, 33, 44, 55, 66}; + static unsigned int copyTexImage2D, readPixels; + copyTexImage2D = 0, readPixels = 0; + + struct Caller { + static void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { + EXPECT_EQ(_target, target); + EXPECT_EQ(_level, level); + EXPECT_EQ(_internalformat, internalformat); + EXPECT_EQ(_x, x); + EXPECT_EQ(_y, y); + EXPECT_EQ(_width, width); + EXPECT_EQ(_height, height); + EXPECT_EQ(_border, border); + copyTexImage2D++; + } + static void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid* pixels) { + EXPECT_EQ(_x, x); + EXPECT_EQ(_y, y); + EXPECT_EQ(_width, width); + EXPECT_EQ(_height, height); + EXPECT_EQ(GL_RGBA, format); + EXPECT_EQ(GL_UNSIGNED_BYTE, type); + ASSERT_TRUE(pixels != NULL); + memcpy(pixels, _pixels, sizeof(_pixels)); + readPixels++; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glCopyTexImage2D = caller.CopyTexImage2D; + hooks.gl.glReadPixels = caller.ReadPixels; + tls.dbg->expectResponse.Bit(msg.glCopyTexImage2D, false); + + Debug_glCopyTexImage2D(_target, _level, _internalformat, _x, _y, _width, _height, + _border); + ASSERT_EQ(1, copyTexImage2D); + ASSERT_EQ(1, readPixels); + + Read(read); + EXPECT_EQ(read.glCopyTexImage2D, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_EQ(_target, read.arg0()); + EXPECT_EQ(_level, read.arg1()); + EXPECT_EQ(_internalformat, read.arg2()); + EXPECT_EQ(_x, read.arg3()); + EXPECT_EQ(_y, read.arg4()); + EXPECT_EQ(_width, read.arg5()); + EXPECT_EQ(_height, read.arg6()); + EXPECT_EQ(_border, read.arg7()); + + EXPECT_TRUE(read.has_data()); + EXPECT_EQ(read.ReferencedImage, read.data_type()); + EXPECT_EQ(GL_RGBA, read.pixel_format()); + EXPECT_EQ(GL_UNSIGNED_BYTE, read.pixel_type()); + uint32_t dataLen = 0; + unsigned char * const data = tls.dbg->Decompress(read.data().data(), + read.data().length(), &dataLen); + ASSERT_EQ(sizeof(_pixels), dataLen); + for (unsigned i = 0; i < sizeof(_pixels) / sizeof(*_pixels); i++) + EXPECT_EQ(_pixels[i], ((const int *)data)[i]) << "xor with 0 ref is identity"; + free(data); + + Read(read); + EXPECT_EQ(read.glCopyTexImage2D, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + + CheckNoAvailable(); +} diff --git a/opengl/libs/egl_tls.h b/opengl/libs/egl_tls.h new file mode 100644 index 0000000000..087989a06e --- /dev/null +++ b/opengl/libs/egl_tls.h @@ -0,0 +1,40 @@ +/* + ** 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 ANDROID_EGL_TLS_H +#define ANDROID_EGL_TLS_H + +#include <EGL/egl.h> + +#include "glesv2dbg.h" + +namespace android +{ +struct tls_t { + tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE), dbg(0) { } + ~tls_t() { + if (dbg) + DestroyDbgContext(dbg); + } + + EGLint error; + EGLContext ctx; + EGLBoolean logCallWithNoContext; + DbgContext* dbg; +}; +} + +#endif diff --git a/opengl/libs/glesv2dbg.h b/opengl/libs/glesv2dbg.h index 8029dcedf9..ee2c0112dd 100644 --- a/opengl/libs/glesv2dbg.h +++ b/opengl/libs/glesv2dbg.h @@ -13,20 +13,27 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ - + #ifndef _GLESV2_DBG_H_ #define _GLESV2_DBG_H_ +#include <pthread.h> + namespace android { - struct DbgContext; - - DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks); - void DestroyDbgContext(DbgContext * const dbg); - - void StartDebugServer(unsigned short port); // create and bind socket if haven't already - void StopDebugServer(); // close socket if open - +struct DbgContext; + +DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey, + const unsigned version, const gl_hooks_t * const hooks); + +void DestroyDbgContext(DbgContext * const dbg); + +// create and bind socket if haven't already, if failed to create socket or +// forceUseFile, then open /data/local/tmp/dump.gles2dbg, exit when size reached +void StartDebugServer(const unsigned short port, const bool forceUseFile, + const unsigned int maxFileSize, const char * const filePath); +void StopDebugServer(); // close socket if open + }; // namespace android #endif // #ifndef _GLESV2_DBG_H_ diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 64cff965b1..a774841ddb 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -93,7 +93,11 @@ int DisplayHardware::getWidth() const { return mWidth; } int DisplayHardware::getHeight() const { return mHeight; } PixelFormat DisplayHardware::getFormat() const { return mFormat; } uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; } -uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; } + +uint32_t DisplayHardware::getMaxViewportDims() const { + return mMaxViewportDims[0] < mMaxViewportDims[1] ? + mMaxViewportDims[0] : mMaxViewportDims[1]; +} void DisplayHardware::init(uint32_t dpy) { @@ -228,7 +232,7 @@ void DisplayHardware::init(uint32_t dpy) eglQueryString(display, EGL_EXTENSIONS)); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); - glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); #ifdef EGL_ANDROID_swap_rectangle @@ -260,7 +264,7 @@ void DisplayHardware::init(uint32_t dpy) LOGI("version : %s", extensions.getVersion()); LOGI("extensions: %s", extensions.getExtension()); LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); - LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); + LOGI("GL_MAX_VIEWPORT_DIMS = %d x %d", mMaxViewportDims[0], mMaxViewportDims[1]); LOGI("flags = %08x", mFlags); // Unbind the context from this thread diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h index ee7a2af80e..cdf89fd0ba 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -108,7 +108,7 @@ private: PixelFormat mFormat; uint32_t mFlags; mutable uint32_t mPageFlipCount; - GLint mMaxViewportDims; + GLint mMaxViewportDims[2]; GLint mMaxTextureSize; HWComposer* mHwc; diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp index 90865da80b..59b7e5a741 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -38,23 +38,10 @@ #include "SurfaceFlinger.h" // ---------------------------------------------------------------------------- -// the sim build doesn't have gettid - -#ifndef HAVE_GETTID -# define gettid getpid -#endif - -// ---------------------------------------------------------------------------- namespace android { -static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep"; -static char const * kWakeFileName = "/sys/power/wait_for_fb_wake"; -static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep"; -static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake"; - -// This dir exists if the framebuffer console is present, either built into -// the kernel or loaded as a module. -static char const * const kFbconSysDir = "/sys/class/graphics/fbcon"; +static char const * const kSleepFileName = "/sys/power/wait_for_fb_sleep"; +static char const * const kWakeFileName = "/sys/power/wait_for_fb_wake"; // ---------------------------------------------------------------------------- @@ -122,237 +109,13 @@ status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const status_t DisplayHardwareBase::DisplayEventThread::readyToRun() { - if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) { - if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) { - LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName); - return NO_INIT; - } - kSleepFileName = kOldSleepFileName; - kWakeFileName = kOldWakeFileName; - } return NO_ERROR; } status_t DisplayHardwareBase::DisplayEventThread::initCheck() const { - return (((access(kSleepFileName, R_OK) == 0 && - access(kWakeFileName, R_OK) == 0) || - (access(kOldSleepFileName, R_OK) == 0 && - access(kOldWakeFileName, R_OK) == 0)) && - access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT; -} - -// ---------------------------------------------------------------------------- - -pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0; - -DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread( - const sp<SurfaceFlinger>& flinger) - : DisplayEventThreadBase(flinger), consoleFd(-1) -{ - sSignalCatcherPid = 0; - - // create a new console - char const * const ttydev = "/dev/tty0"; - int fd = open(ttydev, O_RDWR | O_SYNC); - if (fd<0) { - LOGE("Can't open %s", ttydev); - this->consoleFd = -errno; - return; - } - - // to make sure that we are in text mode - int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT); - if (res<0) { - LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)", - fd, res, strerror(errno)); - } - - // get the current console - struct vt_stat vs; - res = ioctl(fd, VT_GETSTATE, &vs); - if (res<0) { - LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)", - fd, res, strerror(errno)); - this->consoleFd = -errno; - return; - } - - // switch to console 7 (which is what X normaly uses) - int vtnum = 7; - do { - res = ioctl(fd, VT_ACTIVATE, (void*)vtnum); - } while(res < 0 && errno == EINTR); - if (res<0) { - LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d", - fd, errno, strerror(errno), vtnum); - this->consoleFd = -errno; - return; - } - - do { - res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum); - } while(res < 0 && errno == EINTR); - if (res<0) { - LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d", - fd, res, errno, strerror(errno), vtnum); - this->consoleFd = -errno; - return; - } - - // open the new console - close(fd); - fd = open(ttydev, O_RDWR | O_SYNC); - if (fd<0) { - LOGE("Can't open new console %s", ttydev); - this->consoleFd = -errno; - return; - } - - /* disable console line buffer, echo, ... */ - struct termios ttyarg; - ioctl(fd, TCGETS , &ttyarg); - ttyarg.c_iflag = 0; - ttyarg.c_lflag = 0; - ioctl(fd, TCSETS , &ttyarg); - - // set up signals so we're notified when the console changes - // we can't use SIGUSR1 because it's used by the java-vm - vm.mode = VT_PROCESS; - vm.waitv = 0; - vm.relsig = SIGUSR2; - vm.acqsig = SIGUNUSED; - vm.frsig = 0; - - struct sigaction act; - sigemptyset(&act.sa_mask); - act.sa_handler = sigHandler; - act.sa_flags = 0; - sigaction(vm.relsig, &act, NULL); - - sigemptyset(&act.sa_mask); - act.sa_handler = sigHandler; - act.sa_flags = 0; - sigaction(vm.acqsig, &act, NULL); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, vm.relsig); - sigaddset(&mask, vm.acqsig); - sigprocmask(SIG_BLOCK, &mask, NULL); - - // switch to graphic mode - res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS); - LOGW_IF(res<0, - "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res); - - this->prev_vt_num = vs.v_active; - this->vt_num = vtnum; - this->consoleFd = fd; -} - -DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread() -{ - if (this->consoleFd >= 0) { - int fd = this->consoleFd; - int prev_vt_num = this->prev_vt_num; - int res; - ioctl(fd, KDSETMODE, (void*)KD_TEXT); - do { - res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num); - } while(res < 0 && errno == EINTR); - do { - res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num); - } while(res < 0 && errno == EINTR); - close(fd); - char const * const ttydev = "/dev/tty0"; - fd = open(ttydev, O_RDWR | O_SYNC); - ioctl(fd, VT_DISALLOCATE, 0); - close(fd); - } -} - -status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun() -{ - if (this->consoleFd >= 0) { - sSignalCatcherPid = gettid(); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, vm.relsig); - sigaddset(&mask, vm.acqsig); - sigprocmask(SIG_BLOCK, &mask, NULL); - - int res = ioctl(this->consoleFd, VT_SETMODE, &vm); - if (res<0) { - LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)", - this->consoleFd, errno, strerror(errno)); - } - return NO_ERROR; - } - return this->consoleFd; -} - -void DisplayHardwareBase::ConsoleManagerThread::requestExit() -{ - Thread::requestExit(); - if (sSignalCatcherPid != 0) { - // wake the thread up - kill(sSignalCatcherPid, SIGINT); - // wait for it... - } -} - -void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig) -{ - // resend the signal to our signal catcher thread - LOGW("received signal %d in thread %d, resending to %d", - sig, gettid(), sSignalCatcherPid); - - // we absolutely need the delays below because without them - // our main thread never gets a chance to handle the signal. - usleep(10000); - kill(sSignalCatcherPid, sig); - usleep(10000); -} - -status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const -{ - int fd = this->consoleFd; - int err = ioctl(fd, VT_RELDISP, (void*)1); - LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)", - fd, errno, strerror(errno)); - return (err<0) ? (-errno) : status_t(NO_ERROR); -} - -bool DisplayHardwareBase::ConsoleManagerThread::threadLoop() -{ - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, vm.relsig); - sigaddset(&mask, vm.acqsig); - - int sig = 0; - sigwait(&mask, &sig); - - if (sig == vm.relsig) { - sp<SurfaceFlinger> flinger = mFlinger.promote(); - //LOGD("About to give-up screen, flinger = %p", flinger.get()); - if (flinger != 0) - flinger->screenReleased(0); - } else if (sig == vm.acqsig) { - sp<SurfaceFlinger> flinger = mFlinger.promote(); - //LOGD("Screen about to return, flinger = %p", flinger.get()); - if (flinger != 0) - flinger->screenAcquired(0); - } - - return true; -} - -status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const -{ - return consoleFd >= 0 ? NO_ERROR : NO_INIT; + return ((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT; } // ---------------------------------------------------------------------------- @@ -362,10 +125,6 @@ DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, : mCanDraw(true), mScreenAcquired(true) { mDisplayEventThread = new DisplayEventThread(flinger); - if (mDisplayEventThread->initCheck() != NO_ERROR) { - // fall-back on the console - mDisplayEventThread = new ConsoleManagerThread(flinger); - } } DisplayHardwareBase::~DisplayHardwareBase() diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h index fa6a0c4bc5..30eb258779 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h @@ -73,24 +73,6 @@ private: virtual status_t initCheck() const; }; - class ConsoleManagerThread : public DisplayEventThreadBase - { - int consoleFd; - int vt_num; - int prev_vt_num; - vt_mode vm; - static void sigHandler(int sig); - static pid_t sSignalCatcherPid; - public: - ConsoleManagerThread(const sp<SurfaceFlinger>& flinger); - virtual ~ConsoleManagerThread(); - virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void requestExit(); - virtual status_t releaseScreen() const; - virtual status_t initCheck() const; - }; - sp<DisplayEventThreadBase> mDisplayEventThread; mutable int mCanDraw; mutable int mScreenAcquired; |