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 
createTLSKey()34 void createTLSKey() {
35     pthread_key_create(&sTLSKey, (void (*)(void*))&releaseContext);
36 }
37 
getGLTraceContext()38 GLTraceContext *getGLTraceContext() {
39     return (GLTraceContext*) pthread_getspecific(sTLSKey);
40 }
41 
setGLTraceContext(GLTraceContext * c)42 void setGLTraceContext(GLTraceContext *c) {
43     pthread_setspecific(sTLSKey, c);
44 }
45 
setupTraceContextThreadSpecific(GLTraceContext * context)46 void setupTraceContextThreadSpecific(GLTraceContext *context) {
47     pthread_once(&sPthreadOnceKey, createTLSKey);
48     setGLTraceContext(context);
49 }
50 
releaseContext()51 void releaseContext() {
52     GLTraceContext *c = getGLTraceContext();
53     if (c != NULL) {
54         delete c;
55         setGLTraceContext(NULL);
56     }
57 }
58 
GLTraceState(TCPStream * stream)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 
~GLTraceState()69 GLTraceState::~GLTraceState() {
70     if (mStream) {
71         mStream->closeStream();
72         mStream = NULL;
73     }
74 }
75 
getStream()76 TCPStream *GLTraceState::getStream() {
77     return mStream;
78 }
79 
safeSetValue(bool * ptr,bool value,pthread_rwlock_t * lock)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 
safeGetValue(bool * ptr,pthread_rwlock_t * lock)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 
setCollectFbOnEglSwap(bool en)93 void GLTraceState::setCollectFbOnEglSwap(bool en) {
94     safeSetValue(&mCollectFbOnEglSwap, en, &mTraceOptionsRwLock);
95 }
96 
setCollectFbOnGlDraw(bool en)97 void GLTraceState::setCollectFbOnGlDraw(bool en) {
98     safeSetValue(&mCollectFbOnGlDraw, en, &mTraceOptionsRwLock);
99 }
100 
setCollectTextureDataOnGlTexImage(bool en)101 void GLTraceState::setCollectTextureDataOnGlTexImage(bool en) {
102     safeSetValue(&mCollectTextureDataOnGlTexImage, en, &mTraceOptionsRwLock);
103 }
104 
shouldCollectFbOnEglSwap()105 bool GLTraceState::shouldCollectFbOnEglSwap() {
106     return safeGetValue(&mCollectFbOnEglSwap, &mTraceOptionsRwLock);
107 }
108 
shouldCollectFbOnGlDraw()109 bool GLTraceState::shouldCollectFbOnGlDraw() {
110     return safeGetValue(&mCollectFbOnGlDraw, &mTraceOptionsRwLock);
111 }
112 
shouldCollectTextureDataOnGlTexImage()113 bool GLTraceState::shouldCollectTextureDataOnGlTexImage() {
114     return safeGetValue(&mCollectTextureDataOnGlTexImage, &mTraceOptionsRwLock);
115 }
116 
createTraceContext(int version,EGLContext eglContext)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 
getTraceContext(EGLContext c)128 GLTraceContext *GLTraceState::getTraceContext(EGLContext c) {
129     return mPerContextState[c];
130 }
131 
GLTraceContext(int id,int version,GLTraceState * state,BufferedOutputStream * stream)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 
getId()147 int GLTraceContext::getId() {
148     return mId;
149 }
150 
getVersion()151 int GLTraceContext::getVersion() {
152     return mVersion;
153 }
154 
getVersionMajor()155 int GLTraceContext::getVersionMajor() {
156     if (!mVersionParsed) {
157         parseGlesVersion();
158         mVersionParsed = true;
159     }
160     return mVersionMajor;
161 }
162 
getVersionMinor()163 int GLTraceContext::getVersionMinor() {
164     if (!mVersionParsed) {
165         parseGlesVersion();
166         mVersionParsed = true;
167     }
168     return mVersionMinor;
169 }
170 
getGlobalTraceState()171 GLTraceState *GLTraceContext::getGlobalTraceState() {
172     return mState;
173 }
174 
parseGlesVersion()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 
resizeFBMemory(unsigned minSize)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 */
getCompressedFB(void ** fb,unsigned * fbsize,unsigned * fbwidth,unsigned * fbheight,FBBinding fbToRead)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, &currentFb);
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 
traceGLMessage(GLMessage * msg)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 
bindBuffer(GLuint bufferId,GLvoid * data,GLsizeiptr size)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 
getBuffer(GLuint bufferId,GLvoid ** data,GLsizeiptr * size)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 
updateBufferSubData(GLuint bufferId,GLintptr offset,GLvoid * data,GLsizeiptr size)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 
deleteBuffer(GLuint bufferId)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 
ElementArrayBuffer(GLvoid * buf,GLsizeiptr size)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 
~ElementArrayBuffer()299 ElementArrayBuffer::~ElementArrayBuffer() {
300     if (mBuf != NULL) {
301         free(mBuf);
302         mSize = 0;
303     }
304 
305     mBuf = NULL;
306 }
307 
updateSubBuffer(GLintptr offset,const GLvoid * data,GLsizeiptr size)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 
getBuffer()314 GLvoid *ElementArrayBuffer::getBuffer() {
315     return mBuf;
316 }
317 
getSize()318 GLsizeiptr ElementArrayBuffer::getSize() {
319     return mSize;
320 }
321 
322 }; // namespace gltrace
323 }; // namespace android
324