/*
* 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();
}
}