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, &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
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