| /* |
| * 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 <arpa/inet.h> |
| #include <stdlib.h> |
| #include <cutils/log.h> |
| #include <cutils/properties.h> |
| |
| #include "hooks.h" |
| #include "glestrace.h" |
| |
| #include "gltrace_context.h" |
| #include "gltrace_egl.h" |
| #include "gltrace_hooks.h" |
| #include "gltrace_transport.h" |
| |
| namespace android { |
| |
| using gltrace::GLTraceState; |
| using gltrace::GLTraceContext; |
| using gltrace::TCPStream; |
| |
| static pthread_mutex_t sGlTraceStateLock = PTHREAD_MUTEX_INITIALIZER; |
| |
| static int sGlTraceInProgress; |
| static GLTraceState *sGLTraceState; |
| static pthread_t sReceiveThreadId; |
| |
| /** |
| * Task that monitors the control stream from the host and updates |
| * the trace status according to commands received from the host. |
| */ |
| static void *commandReceiveTask(void *arg) { |
| GLTraceState *state = (GLTraceState *)arg; |
| TCPStream *stream = state->getStream(); |
| |
| // The control stream always receives an integer size of the |
| // command buffer, followed by the actual command buffer. |
| uint32_t cmdSize; |
| |
| // Command Buffer |
| void *cmdBuf = NULL; |
| uint32_t cmdBufSize = 0; |
| |
| enum TraceSettingsMasks { |
| READ_FB_ON_EGLSWAP_MASK = 1 << 0, |
| READ_FB_ON_GLDRAW_MASK = 1 << 1, |
| READ_TEXTURE_DATA_ON_GLTEXIMAGE_MASK = 1 << 2, |
| }; |
| |
| while (true) { |
| // read command size |
| if (stream->receive(&cmdSize, sizeof(uint32_t)) < 0) { |
| break; |
| } |
| cmdSize = ntohl(cmdSize); |
| |
| // ensure command buffer is of required size |
| if (cmdBufSize < cmdSize) { |
| free(cmdBuf); |
| cmdBufSize = cmdSize; |
| cmdBuf = malloc(cmdSize); |
| if (cmdBuf == NULL) |
| break; |
| } |
| |
| // receive the command |
| if (stream->receive(cmdBuf, cmdSize) < 0) { |
| break; |
| } |
| |
| if (cmdSize != sizeof(uint32_t)) { |
| // Currently, we only support commands that are a single integer, |
| // so we skip all other commands |
| continue; |
| } |
| |
| uint32_t cmd = ntohl(*(uint32_t*)cmdBuf); |
| |
| bool collectFbOnEglSwap = (cmd & READ_FB_ON_EGLSWAP_MASK) != 0; |
| bool collectFbOnGlDraw = (cmd & READ_FB_ON_GLDRAW_MASK) != 0; |
| bool collectTextureData = (cmd & READ_TEXTURE_DATA_ON_GLTEXIMAGE_MASK) != 0; |
| |
| state->setCollectFbOnEglSwap(collectFbOnEglSwap); |
| state->setCollectFbOnGlDraw(collectFbOnGlDraw); |
| state->setCollectTextureDataOnGlTexImage(collectTextureData); |
| |
| ALOGD("trace options: eglswap: %d, gldraw: %d, texImage: %d", |
| collectFbOnEglSwap, collectFbOnGlDraw, collectTextureData); |
| } |
| |
| ALOGE("Stopping OpenGL Trace Command Receiver\n"); |
| |
| free(cmdBuf); |
| return NULL; |
| } |
| |
| /** |
| * Starts Trace Server and waits for connection from the host. |
| * Returns -1 in case of connection error, 0 otherwise. |
| */ |
| int GLTrace_start() { |
| int status = 0; |
| int clientSocket = -1; |
| TCPStream *stream = NULL; |
| |
| pthread_mutex_lock(&sGlTraceStateLock); |
| |
| if (sGlTraceInProgress) { |
| goto done; |
| } |
| |
| char udsName[PROPERTY_VALUE_MAX]; |
| property_get("debug.egl.debug_portname", udsName, "gltrace"); |
| clientSocket = gltrace::acceptClientConnection(udsName); |
| if (clientSocket < 0) { |
| ALOGE("Error creating GLTrace server socket. Tracing disabled."); |
| status = -1; |
| goto done; |
| } |
| |
| sGlTraceInProgress = 1; |
| |
| // create communication channel to the host |
| stream = new TCPStream(clientSocket); |
| |
| // initialize tracing state |
| sGLTraceState = new GLTraceState(stream); |
| |
| pthread_create(&sReceiveThreadId, NULL, commandReceiveTask, sGLTraceState); |
| |
| done: |
| pthread_mutex_unlock(&sGlTraceStateLock); |
| return status; |
| } |
| |
| void GLTrace_stop() { |
| pthread_mutex_lock(&sGlTraceStateLock); |
| |
| if (sGlTraceInProgress) { |
| sGlTraceInProgress = 0; |
| delete sGLTraceState; |
| sGLTraceState = NULL; |
| } |
| |
| pthread_mutex_unlock(&sGlTraceStateLock); |
| } |
| |
| void GLTrace_eglCreateContext(int version, EGLContext c) { |
| pthread_mutex_lock(&sGlTraceStateLock); |
| GLTraceState *state = sGLTraceState; |
| pthread_mutex_unlock(&sGlTraceStateLock); |
| |
| if (state == NULL) return; |
| |
| // update trace state for new EGL context |
| GLTraceContext *traceContext = state->createTraceContext(version, c); |
| gltrace::setupTraceContextThreadSpecific(traceContext); |
| |
| // trace command through to the host |
| gltrace::GLTrace_eglCreateContext(version, traceContext->getId()); |
| } |
| |
| void GLTrace_eglMakeCurrent(const unsigned version, gl_hooks_t *hooks, EGLContext c) { |
| pthread_mutex_lock(&sGlTraceStateLock); |
| GLTraceState *state = sGLTraceState; |
| pthread_mutex_unlock(&sGlTraceStateLock); |
| |
| if (state == NULL) return; |
| |
| // setup per context state |
| GLTraceContext *traceContext = state->getTraceContext(c); |
| if (traceContext == NULL) { |
| GLTrace_eglCreateContext(version, c); |
| traceContext = state->getTraceContext(c); |
| } |
| |
| traceContext->hooks = hooks; |
| gltrace::setupTraceContextThreadSpecific(traceContext); |
| |
| // trace command through to the host |
| gltrace::GLTrace_eglMakeCurrent(traceContext->getId()); |
| } |
| |
| void GLTrace_eglReleaseThread() { |
| gltrace::releaseContext(); |
| } |
| |
| void GLTrace_eglSwapBuffers(void *dpy, void *draw) { |
| gltrace::GLTrace_eglSwapBuffers(dpy, draw); |
| } |
| |
| gl_hooks_t *GLTrace_getGLHooks() { |
| return gltrace::getGLHooks(); |
| } |
| |
| } |