1/* 2 * Copyright 2011, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <pthread.h> 18#include <cutils/log.h> 19 20extern "C" { 21#include "liblzf/lzf.h" 22} 23 24#include "gltrace_context.h" 25 26namespace android { 27namespace gltrace { 28 29using ::android::gl_hooks_t; 30 31static pthread_key_t sTLSKey = -1; 32static pthread_once_t sPthreadOnceKey = PTHREAD_ONCE_INIT; 33 34void createTLSKey() { 35 pthread_key_create(&sTLSKey, (void (*)(void*))&releaseContext); 36} 37 38GLTraceContext *getGLTraceContext() { 39 return (GLTraceContext*) pthread_getspecific(sTLSKey); 40} 41 42void setGLTraceContext(GLTraceContext *c) { 43 pthread_setspecific(sTLSKey, c); 44} 45 46void setupTraceContextThreadSpecific(GLTraceContext *context) { 47 pthread_once(&sPthreadOnceKey, createTLSKey); 48 setGLTraceContext(context); 49} 50 51void releaseContext() { 52 GLTraceContext *c = getGLTraceContext(); 53 if (c != NULL) { 54 delete c; 55 setGLTraceContext(NULL); 56 } 57} 58 59GLTraceState::GLTraceState(TCPStream *stream) { 60 mTraceContextIds = 0; 61 mStream = stream; 62 63 mCollectFbOnEglSwap = false; 64 mCollectFbOnGlDraw = false; 65 mCollectTextureDataOnGlTexImage = false; 66 pthread_rwlock_init(&mTraceOptionsRwLock, NULL); 67} 68 69GLTraceState::~GLTraceState() { 70 if (mStream) { 71 mStream->closeStream(); 72 mStream = NULL; 73 } 74} 75 76TCPStream *GLTraceState::getStream() { 77 return mStream; 78} 79 80void GLTraceState::safeSetValue(bool *ptr, bool value, pthread_rwlock_t *lock) { 81 pthread_rwlock_wrlock(lock); 82 *ptr = value; 83 pthread_rwlock_unlock(lock); 84} 85 86bool GLTraceState::safeGetValue(bool *ptr, pthread_rwlock_t *lock) { 87 pthread_rwlock_rdlock(lock); 88 bool value = *ptr; 89 pthread_rwlock_unlock(lock); 90 return value; 91} 92 93void GLTraceState::setCollectFbOnEglSwap(bool en) { 94 safeSetValue(&mCollectFbOnEglSwap, en, &mTraceOptionsRwLock); 95} 96 97void GLTraceState::setCollectFbOnGlDraw(bool en) { 98 safeSetValue(&mCollectFbOnGlDraw, en, &mTraceOptionsRwLock); 99} 100 101void GLTraceState::setCollectTextureDataOnGlTexImage(bool en) { 102 safeSetValue(&mCollectTextureDataOnGlTexImage, en, &mTraceOptionsRwLock); 103} 104 105bool GLTraceState::shouldCollectFbOnEglSwap() { 106 return safeGetValue(&mCollectFbOnEglSwap, &mTraceOptionsRwLock); 107} 108 109bool GLTraceState::shouldCollectFbOnGlDraw() { 110 return safeGetValue(&mCollectFbOnGlDraw, &mTraceOptionsRwLock); 111} 112 113bool GLTraceState::shouldCollectTextureDataOnGlTexImage() { 114 return safeGetValue(&mCollectTextureDataOnGlTexImage, &mTraceOptionsRwLock); 115} 116 117GLTraceContext *GLTraceState::createTraceContext(int version, EGLContext eglContext) { 118 int id = __sync_fetch_and_add(&mTraceContextIds, 1); 119 120 const size_t DEFAULT_BUFFER_SIZE = 8192; 121 BufferedOutputStream *stream = new BufferedOutputStream(mStream, DEFAULT_BUFFER_SIZE); 122 GLTraceContext *traceContext = new GLTraceContext(id, version, this, stream); 123 mPerContextState[eglContext] = traceContext; 124 125 return traceContext; 126} 127 128GLTraceContext *GLTraceState::getTraceContext(EGLContext c) { 129 return mPerContextState[c]; 130} 131 132GLTraceContext::GLTraceContext(int id, int version, GLTraceState *state, 133 BufferedOutputStream *stream) : 134 mId(id), 135 mVersion(version), 136 mVersionMajor(0), 137 mVersionMinor(0), 138 mVersionParsed(false), 139 mState(state), 140 mBufferedOutputStream(stream), 141 mElementArrayBuffers(DefaultKeyedVector<GLuint, ElementArrayBuffer*>(NULL)) 142{ 143 fbcontents = fbcompressed = NULL; 144 fbcontentsSize = 0; 145} 146 147int GLTraceContext::getId() { 148 return mId; 149} 150 151int GLTraceContext::getVersion() { 152 return mVersion; 153} 154 155int GLTraceContext::getVersionMajor() { 156 if (!mVersionParsed) { 157 parseGlesVersion(); 158 mVersionParsed = true; 159 } 160 return mVersionMajor; 161} 162 163int GLTraceContext::getVersionMinor() { 164 if (!mVersionParsed) { 165 parseGlesVersion(); 166 mVersionParsed = true; 167 } 168 return mVersionMinor; 169} 170 171GLTraceState *GLTraceContext::getGlobalTraceState() { 172 return mState; 173} 174 175void GLTraceContext::parseGlesVersion() { 176 const char* str = (const char*)hooks->gl.glGetString(GL_VERSION); 177 int major, minor; 178 if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { 179 if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { 180 ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); 181 major = 1; 182 minor = 0; 183 } 184 } 185 mVersionMajor = major; 186 mVersionMinor = minor; 187} 188 189void GLTraceContext::resizeFBMemory(unsigned minSize) { 190 if (fbcontentsSize >= minSize) { 191 return; 192 } 193 194 if (fbcontents != NULL) { 195 free(fbcontents); 196 free(fbcompressed); 197 } 198 199 fbcontents = malloc(minSize); 200 fbcompressed = malloc(minSize); 201 202 fbcontentsSize = minSize; 203} 204 205/** obtain a pointer to the compressed framebuffer image */ 206void GLTraceContext::getCompressedFB(void **fb, unsigned *fbsize, unsigned *fbwidth, 207 unsigned *fbheight, FBBinding fbToRead) { 208 int viewport[4] = {}; 209 hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport); 210 unsigned fbContentsSize = viewport[2] * viewport[3] * 4; 211 212 resizeFBMemory(fbContentsSize); 213 214 // switch current framebuffer binding if necessary 215 GLint currentFb = -1; 216 bool fbSwitched = false; 217 if (fbToRead != CURRENTLY_BOUND_FB) { 218 hooks->gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFb); 219 220 if (currentFb != 0) { 221 hooks->gl.glBindFramebuffer(GL_FRAMEBUFFER, 0); 222 fbSwitched = true; 223 } 224 } 225 226 hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], 227 GL_RGBA, GL_UNSIGNED_BYTE, fbcontents); 228 229 // switch back to previously bound buffer if necessary 230 if (fbSwitched) { 231 hooks->gl.glBindFramebuffer(GL_FRAMEBUFFER, currentFb); 232 } 233 234 *fbsize = lzf_compress(fbcontents, fbContentsSize, fbcompressed, fbContentsSize); 235 *fb = fbcompressed; 236 *fbwidth = viewport[2]; 237 *fbheight = viewport[3]; 238} 239 240void GLTraceContext::traceGLMessage(GLMessage *msg) { 241 mBufferedOutputStream->send(msg); 242 243 GLMessage_Function func = msg->function(); 244 if (func == GLMessage::eglSwapBuffers 245 || func == GLMessage::eglCreateContext 246 || func == GLMessage::eglMakeCurrent 247 || func == GLMessage::glDrawArrays 248 || func == GLMessage::glDrawElements) { 249 mBufferedOutputStream->flush(); 250 } 251} 252 253void GLTraceContext::bindBuffer(GLuint bufferId, GLvoid *data, GLsizeiptr size) { 254 // free previously bound buffer if any 255 ElementArrayBuffer *oldBuffer = mElementArrayBuffers.valueFor(bufferId); 256 if (oldBuffer != NULL) { 257 delete oldBuffer; 258 } 259 260 mElementArrayBuffers.add(bufferId, new ElementArrayBuffer(data, size)); 261} 262 263void GLTraceContext::getBuffer(GLuint bufferId, GLvoid **data, GLsizeiptr *size) { 264 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 265 if (buffer == NULL) { 266 *data = NULL; 267 *size = 0; 268 } else { 269 *data = buffer->getBuffer(); 270 *size = buffer->getSize(); 271 } 272} 273 274void GLTraceContext::updateBufferSubData(GLuint bufferId, GLintptr offset, GLvoid *data, 275 GLsizeiptr size) { 276 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 277 if (buffer != NULL) { 278 buffer->updateSubBuffer(offset, data, size); 279 } 280} 281 282void GLTraceContext::deleteBuffer(GLuint bufferId) { 283 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 284 if (buffer != NULL) { 285 delete buffer; 286 mElementArrayBuffers.removeItem(bufferId); 287 } 288} 289 290ElementArrayBuffer::ElementArrayBuffer(GLvoid *buf, GLsizeiptr size) { 291 mBuf = malloc(size); 292 mSize = size; 293 294 if (buf != NULL) { 295 memcpy(mBuf, buf, size); 296 } 297} 298 299ElementArrayBuffer::~ElementArrayBuffer() { 300 if (mBuf != NULL) { 301 free(mBuf); 302 mSize = 0; 303 } 304 305 mBuf = NULL; 306} 307 308void ElementArrayBuffer::updateSubBuffer(GLintptr offset, const GLvoid* data, GLsizeiptr size) { 309 if (offset + size <= mSize) { 310 memcpy((char*)mBuf + offset, data, size); 311 } 312} 313 314GLvoid *ElementArrayBuffer::getBuffer() { 315 return mBuf; 316} 317 318GLsizeiptr ElementArrayBuffer::getSize() { 319 return mSize; 320} 321 322}; // namespace gltrace 323}; // namespace android 324