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 20 extern "C" { 21 #include "liblzf/lzf.h" 22 } 23 24 #include "gltrace_context.h" 25 26 namespace android { 27 namespace gltrace { 28 29 using ::android::gl_hooks_t; 30 31 static pthread_key_t sTLSKey = -1; 32 static pthread_once_t sPthreadOnceKey = PTHREAD_ONCE_INIT; 33 34 void createTLSKey() { 35 pthread_key_create(&sTLSKey, (void (*)(void*))&releaseContext); 36 } 37 38 GLTraceContext *getGLTraceContext() { 39 return (GLTraceContext*) pthread_getspecific(sTLSKey); 40 } 41 42 void setGLTraceContext(GLTraceContext *c) { 43 pthread_setspecific(sTLSKey, c); 44 } 45 46 void setupTraceContextThreadSpecific(GLTraceContext *context) { 47 pthread_once(&sPthreadOnceKey, createTLSKey); 48 setGLTraceContext(context); 49 } 50 51 void releaseContext() { 52 GLTraceContext *c = getGLTraceContext(); 53 if (c != NULL) { 54 delete c; 55 setGLTraceContext(NULL); 56 } 57 } 58 59 GLTraceState::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 69 GLTraceState::~GLTraceState() { 70 if (mStream) { 71 mStream->closeStream(); 72 mStream = NULL; 73 } 74 } 75 76 TCPStream *GLTraceState::getStream() { 77 return mStream; 78 } 79 80 void 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 86 bool 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 93 void GLTraceState::setCollectFbOnEglSwap(bool en) { 94 safeSetValue(&mCollectFbOnEglSwap, en, &mTraceOptionsRwLock); 95 } 96 97 void GLTraceState::setCollectFbOnGlDraw(bool en) { 98 safeSetValue(&mCollectFbOnGlDraw, en, &mTraceOptionsRwLock); 99 } 100 101 void GLTraceState::setCollectTextureDataOnGlTexImage(bool en) { 102 safeSetValue(&mCollectTextureDataOnGlTexImage, en, &mTraceOptionsRwLock); 103 } 104 105 bool GLTraceState::shouldCollectFbOnEglSwap() { 106 return safeGetValue(&mCollectFbOnEglSwap, &mTraceOptionsRwLock); 107 } 108 109 bool GLTraceState::shouldCollectFbOnGlDraw() { 110 return safeGetValue(&mCollectFbOnGlDraw, &mTraceOptionsRwLock); 111 } 112 113 bool GLTraceState::shouldCollectTextureDataOnGlTexImage() { 114 return safeGetValue(&mCollectTextureDataOnGlTexImage, &mTraceOptionsRwLock); 115 } 116 117 GLTraceContext *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 128 GLTraceContext *GLTraceState::getTraceContext(EGLContext c) { 129 return mPerContextState[c]; 130 } 131 132 GLTraceContext::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 147 int GLTraceContext::getId() { 148 return mId; 149 } 150 151 int GLTraceContext::getVersion() { 152 return mVersion; 153 } 154 155 int GLTraceContext::getVersionMajor() { 156 if (!mVersionParsed) { 157 parseGlesVersion(); 158 mVersionParsed = true; 159 } 160 return mVersionMajor; 161 } 162 163 int GLTraceContext::getVersionMinor() { 164 if (!mVersionParsed) { 165 parseGlesVersion(); 166 mVersionParsed = true; 167 } 168 return mVersionMinor; 169 } 170 171 GLTraceState *GLTraceContext::getGlobalTraceState() { 172 return mState; 173 } 174 175 void 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 189 void 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 */ 206 void 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 240 void 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 253 void 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 263 void 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 274 void 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 282 void GLTraceContext::deleteBuffer(GLuint bufferId) { 283 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 284 if (buffer != NULL) { 285 delete buffer; 286 mElementArrayBuffers.removeItem(bufferId); 287 } 288 } 289 290 ElementArrayBuffer::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 299 ElementArrayBuffer::~ElementArrayBuffer() { 300 if (mBuf != NULL) { 301 free(mBuf); 302 mSize = 0; 303 } 304 305 mBuf = NULL; 306 } 307 308 void ElementArrayBuffer::updateSubBuffer(GLintptr offset, const GLvoid* data, GLsizeiptr size) { 309 if (offset + size <= mSize) { 310 memcpy((char*)mBuf + offset, data, size); 311 } 312 } 313 314 GLvoid *ElementArrayBuffer::getBuffer() { 315 return mBuf; 316 } 317 318 GLsizeiptr ElementArrayBuffer::getSize() { 319 return mSize; 320 } 321 322 }; // namespace gltrace 323 }; // namespace android 324