gltrace_context.cpp revision 4e620ddce344e946ced992f61a69c367ff92fe24
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    mState(state),
137    mBufferedOutputStream(stream),
138    mElementArrayBuffers(DefaultKeyedVector<GLuint, ElementArrayBuffer*>(NULL))
139{
140    fbcontents = fbcompressed = NULL;
141    fbcontentsSize = 0;
142}
143
144int GLTraceContext::getId() {
145    return mId;
146}
147
148int GLTraceContext::getVersion() {
149    return mVersion;
150}
151
152GLTraceState *GLTraceContext::getGlobalTraceState() {
153    return mState;
154}
155
156void 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 */
173void 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, &currentFb);
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
207void 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
220void 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
230void 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
241void 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
249void GLTraceContext::deleteBuffer(GLuint bufferId) {
250    ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId);
251    if (buffer != NULL) {
252        delete buffer;
253        mElementArrayBuffers.removeItem(bufferId);
254    }
255}
256
257ElementArrayBuffer::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
266ElementArrayBuffer::~ElementArrayBuffer() {
267    if (mBuf != NULL) {
268        free(mBuf);
269        mSize = 0;
270    }
271
272    mBuf = NULL;
273}
274
275void ElementArrayBuffer::updateSubBuffer(GLintptr offset, const GLvoid* data, GLsizeiptr size) {
276    if (offset + size <= mSize) {
277        memcpy((char*)mBuf + offset, data, size);
278    }
279}
280
281GLvoid *ElementArrayBuffer::getBuffer() {
282    return mBuf;
283}
284
285GLsizeiptr ElementArrayBuffer::getSize() {
286    return mSize;
287}
288
289}; // namespace gltrace
290}; // namespace android
291