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, NULL); 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 mState(state), 137 mBufferedOutputStream(stream), 138 mElementArrayBuffers(DefaultKeyedVector<GLuint, ElementArrayBuffer*>(NULL)) 139 { 140 fbcontents = fbcompressed = NULL; 141 fbcontentsSize = 0; 142 } 143 144 int GLTraceContext::getId() { 145 return mId; 146 } 147 148 int GLTraceContext::getVersion() { 149 return mVersion; 150 } 151 152 GLTraceState *GLTraceContext::getGlobalTraceState() { 153 return mState; 154 } 155 156 void GLTraceContext::resizeFBMemory(unsigned minSize) { 157 if (fbcontentsSize >= minSize) { 158 return; 159 } 160 161 if (fbcontents != NULL) { 162 free(fbcontents); 163 free(fbcompressed); 164 } 165 166 fbcontents = malloc(minSize); 167 fbcompressed = malloc(minSize); 168 169 fbcontentsSize = minSize; 170 } 171 172 /** obtain a pointer to the compressed framebuffer image */ 173 void GLTraceContext::getCompressedFB(void **fb, unsigned *fbsize, unsigned *fbwidth, 174 unsigned *fbheight, FBBinding fbToRead) { 175 int viewport[4] = {}; 176 hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport); 177 unsigned fbContentsSize = viewport[2] * viewport[3] * 4; 178 179 resizeFBMemory(fbContentsSize); 180 181 // switch current framebuffer binding if necessary 182 GLint currentFb = -1; 183 bool fbSwitched = false; 184 if (fbToRead != CURRENTLY_BOUND_FB) { 185 hooks->gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFb); 186 187 if (currentFb != 0) { 188 hooks->gl.glBindFramebuffer(GL_FRAMEBUFFER, 0); 189 fbSwitched = true; 190 } 191 } 192 193 hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], 194 GL_RGBA, GL_UNSIGNED_BYTE, fbcontents); 195 196 // switch back to previously bound buffer if necessary 197 if (fbSwitched) { 198 hooks->gl.glBindFramebuffer(GL_FRAMEBUFFER, currentFb); 199 } 200 201 *fbsize = lzf_compress(fbcontents, fbContentsSize, fbcompressed, fbContentsSize); 202 *fb = fbcompressed; 203 *fbwidth = viewport[2]; 204 *fbheight = viewport[3]; 205 } 206 207 void GLTraceContext::traceGLMessage(GLMessage *msg) { 208 mBufferedOutputStream->send(msg); 209 210 GLMessage_Function func = msg->function(); 211 if (func == GLMessage::eglSwapBuffers 212 || func == GLMessage::eglCreateContext 213 || func == GLMessage::eglMakeCurrent 214 || func == GLMessage::glDrawArrays 215 || func == GLMessage::glDrawElements) { 216 mBufferedOutputStream->flush(); 217 } 218 } 219 220 void GLTraceContext::bindBuffer(GLuint bufferId, GLvoid *data, GLsizeiptr size) { 221 // free previously bound buffer if any 222 ElementArrayBuffer *oldBuffer = mElementArrayBuffers.valueFor(bufferId); 223 if (oldBuffer != NULL) { 224 delete oldBuffer; 225 } 226 227 mElementArrayBuffers.add(bufferId, new ElementArrayBuffer(data, size)); 228 } 229 230 void GLTraceContext::getBuffer(GLuint bufferId, GLvoid **data, GLsizeiptr *size) { 231 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 232 if (buffer == NULL) { 233 *data = NULL; 234 *size = 0; 235 } else { 236 *data = buffer->getBuffer(); 237 *size = buffer->getSize(); 238 } 239 } 240 241 void GLTraceContext::updateBufferSubData(GLuint bufferId, GLintptr offset, GLvoid *data, 242 GLsizeiptr size) { 243 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 244 if (buffer != NULL) { 245 buffer->updateSubBuffer(offset, data, size); 246 } 247 } 248 249 void GLTraceContext::deleteBuffer(GLuint bufferId) { 250 ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId); 251 if (buffer != NULL) { 252 delete buffer; 253 mElementArrayBuffers.removeItem(bufferId); 254 } 255 } 256 257 ElementArrayBuffer::ElementArrayBuffer(GLvoid *buf, GLsizeiptr size) { 258 mBuf = malloc(size); 259 mSize = size; 260 261 if (buf != NULL) { 262 memcpy(mBuf, buf, size); 263 } 264 } 265 266 ElementArrayBuffer::~ElementArrayBuffer() { 267 if (mBuf != NULL) { 268 free(mBuf); 269 mSize = 0; 270 } 271 272 mBuf = NULL; 273 } 274 275 void ElementArrayBuffer::updateSubBuffer(GLintptr offset, const GLvoid* data, GLsizeiptr size) { 276 if (offset + size <= mSize) { 277 memcpy((char*)mBuf + offset, data, size); 278 } 279 } 280 281 GLvoid *ElementArrayBuffer::getBuffer() { 282 return mBuf; 283 } 284 285 GLsizeiptr ElementArrayBuffer::getSize() { 286 return mSize; 287 } 288 289 }; // namespace gltrace 290 }; // namespace android 291