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 "header.h"
18
19extern "C" {
20#include "liblzf/lzf.h"
21}
22
23namespace android {
24
25static pthread_key_t dbgEGLThreadLocalStorageKey = -1;
26static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER;
27
28DbgContext * getDbgContextThreadSpecific() {
29    return (DbgContext*)pthread_getspecific(dbgEGLThreadLocalStorageKey);
30}
31
32DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
33                       const unsigned MAX_VERTEX_ATTRIBS)
34        : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0)
35        , version(version), hooks(hooks)
36        , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
37        , readBytesPerPixel(4)
38        , captureSwap(0), captureDraw(0)
39        , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
40        , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
41        , program(0), maxAttrib(0)
42{
43    lzf_ref[0] = lzf_ref[1] = NULL;
44    for (unsigned i = 0; i < MAX_VERTEX_ATTRIBS; i++)
45        vertexAttribs[i] = VertexAttrib();
46    memset(&expectResponse, 0, sizeof(expectResponse));
47}
48
49DbgContext::~DbgContext()
50{
51    delete vertexAttribs;
52    free(lzf_buf);
53    free(lzf_ref[0]);
54    free(lzf_ref[1]);
55}
56
57DbgContext* CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks)
58{
59    pthread_mutex_lock(&gThreadLocalStorageKeyMutex);
60    if (dbgEGLThreadLocalStorageKey == -1)
61        pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
62    pthread_mutex_unlock(&gThreadLocalStorageKeyMutex);
63
64    assert(version < 2);
65    assert(GL_NO_ERROR == hooks->gl.glGetError());
66    GLint MAX_VERTEX_ATTRIBS = 0;
67    hooks->gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &MAX_VERTEX_ATTRIBS);
68    DbgContext* dbg = new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
69    glesv2debugger::Message msg, cmd;
70    msg.set_context_id(reinterpret_cast<int>(dbg));
71    msg.set_expect_response(false);
72    msg.set_type(msg.Response);
73    msg.set_function(msg.SETPROP);
74    msg.set_prop(msg.GLConstant);
75    msg.set_arg0(GL_MAX_VERTEX_ATTRIBS);
76    msg.set_arg1(MAX_VERTEX_ATTRIBS);
77    Send(msg, cmd);
78
79    GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0;
80    hooks->gl.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &MAX_COMBINED_TEXTURE_IMAGE_UNITS);
81    msg.set_arg0(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
82    msg.set_arg1(MAX_COMBINED_TEXTURE_IMAGE_UNITS);
83    Send(msg, cmd);
84
85    pthread_setspecific(dbgEGLThreadLocalStorageKey, dbg);
86    return dbg;
87}
88
89void dbgReleaseThread() {
90    delete getDbgContextThreadSpecific();
91}
92
93unsigned GetBytesPerPixel(const GLenum format, const GLenum type)
94{
95    switch (type) {
96    case GL_UNSIGNED_SHORT_5_6_5:
97    case GL_UNSIGNED_SHORT_4_4_4_4:
98    case GL_UNSIGNED_SHORT_5_5_5_1:
99        return 2;
100    case GL_UNSIGNED_BYTE:
101        break;
102    default:
103        LOGE("GetBytesPerPixel: unknown type %x", type);
104    }
105
106    switch (format) {
107    case GL_ALPHA:
108    case GL_LUMINANCE:
109        return 1;
110    case GL_LUMINANCE_ALPHA:
111        return 2;
112    case GL_RGB:
113        return 3;
114    case GL_RGBA:
115    case 0x80E1:   // GL_BGRA_EXT
116        return 4;
117    default:
118        LOGE("GetBytesPerPixel: unknown format %x", format);
119    }
120
121    return 1; // in doubt...
122}
123
124void DbgContext::Fetch(const unsigned index, std::string * const data) const
125{
126    // VBO data is already on client, just send user pointer data
127    for (unsigned i = 0; i < maxAttrib; i++) {
128        if (!vertexAttribs[i].enabled)
129            continue;
130        if (vertexAttribs[i].buffer > 0)
131            continue;
132        const char * ptr = (const char *)vertexAttribs[i].ptr;
133        ptr += index * vertexAttribs[i].stride;
134        data->append(ptr, vertexAttribs[i].elemSize);
135    }
136}
137
138void DbgContext::Compress(const void * in_data, unsigned int in_len,
139                          std::string * const outStr)
140{
141    if (!lzf_buf)
142        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
143    assert(lzf_buf);
144    const uint32_t totalDecompSize = in_len;
145    outStr->append((const char *)&totalDecompSize, sizeof(totalDecompSize));
146    for (unsigned int i = 0; i < in_len; i += LZF_CHUNK_SIZE) {
147        uint32_t chunkSize = LZF_CHUNK_SIZE;
148        if (i + LZF_CHUNK_SIZE > in_len)
149            chunkSize = in_len - i;
150        const uint32_t compSize = lzf_compress((const char *)in_data + i, chunkSize,
151                                               lzf_buf, LZF_CHUNK_SIZE);
152        outStr->append((const char *)&chunkSize, sizeof(chunkSize));
153        outStr->append((const char *)&compSize, sizeof(compSize));
154        if (compSize > 0)
155            outStr->append(lzf_buf, compSize);
156        else // compressed chunk bigger than LZF_CHUNK_SIZE (and uncompressed)
157            outStr->append((const char *)in_data + i, chunkSize);
158    }
159}
160
161unsigned char * DbgContext::Decompress(const void * in, const unsigned int inLen,
162                                       unsigned int * const outLen)
163{
164    assert(inLen > 4 * 3);
165    if (inLen < 4 * 3)
166        return NULL;
167    *outLen = *(uint32_t *)in;
168    unsigned char * const out = (unsigned char *)malloc(*outLen);
169    unsigned int outPos = 0;
170    const unsigned char * const end = (const unsigned char *)in + inLen;
171    for (const unsigned char * inData = (const unsigned char *)in + 4; inData < end; ) {
172        const uint32_t chunkOut = *(uint32_t *)inData;
173        inData += 4;
174        const uint32_t chunkIn = *(uint32_t *)inData;
175        inData += 4;
176        if (chunkIn > 0) {
177            assert(inData + chunkIn <= end);
178            assert(outPos + chunkOut <= *outLen);
179            outPos += lzf_decompress(inData, chunkIn, out + outPos, chunkOut);
180            inData += chunkIn;
181        } else {
182            assert(inData + chunkOut <= end);
183            assert(outPos + chunkOut <= *outLen);
184            memcpy(out + outPos, inData, chunkOut);
185            inData += chunkOut;
186            outPos += chunkOut;
187        }
188    }
189    return out;
190}
191
192void * DbgContext::GetReadPixelsBuffer(const unsigned size)
193{
194    if (lzf_refBufSize < size + 8) {
195        lzf_refBufSize = size + 8;
196        lzf_ref[0] = (unsigned *)realloc(lzf_ref[0], lzf_refBufSize);
197        assert(lzf_ref[0]);
198        memset(lzf_ref[0], 0, lzf_refBufSize);
199        lzf_ref[1] = (unsigned *)realloc(lzf_ref[1], lzf_refBufSize);
200        assert(lzf_ref[1]);
201        memset(lzf_ref[1], 0, lzf_refBufSize);
202    }
203    if (lzf_refSize != size) // need to clear unused ref to maintain consistency
204    { // since ref and src are swapped each time
205        memset((char *)lzf_ref[0] + lzf_refSize, 0, lzf_refBufSize - lzf_refSize);
206        memset((char *)lzf_ref[1] + lzf_refSize, 0, lzf_refBufSize - lzf_refSize);
207    }
208    lzf_refSize = size;
209    lzf_readIndex ^= 1;
210    return lzf_ref[lzf_readIndex];
211}
212
213void DbgContext::CompressReadPixelBuffer(std::string * const outStr)
214{
215    assert(lzf_ref[0] && lzf_ref[1]);
216    unsigned * const ref = lzf_ref[lzf_readIndex ^ 1];
217    unsigned * const src = lzf_ref[lzf_readIndex];
218    for (unsigned i = 0; i < lzf_refSize / sizeof(*ref) + 1; i++)
219        ref[i] ^= src[i];
220    Compress(ref, lzf_refSize, outStr);
221}
222
223char * DbgContext::GetBuffer()
224{
225    if (!lzf_buf)
226        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
227    assert(lzf_buf);
228    return lzf_buf;
229}
230
231unsigned int DbgContext::GetBufferSize()
232{
233    if (!lzf_buf)
234        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
235    assert(lzf_buf);
236    if (lzf_buf)
237        return LZF_CHUNK_SIZE;
238    else
239        return 0;
240}
241
242void DbgContext::glUseProgram(GLuint program)
243{
244    while (GLenum error = hooks->gl.glGetError())
245        LOGD("DbgContext::glUseProgram(%u): before glGetError() = 0x%.4X",
246             program, error);
247    this->program = program;
248    maxAttrib = 0;
249    if (program == 0)
250        return;
251    GLint activeAttributes = 0;
252    hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
253    maxAttrib = 0;
254    GLint maxNameLen = -1;
255    hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLen);
256    char * name = new char [maxNameLen + 1];
257    name[maxNameLen] = 0;
258    // find total number of attribute slots used
259    for (unsigned i = 0; i < activeAttributes; i++) {
260        GLint size = -1;
261        GLenum type = -1;
262        hooks->gl.glGetActiveAttrib(program, i, maxNameLen + 1, NULL, &size, &type, name);
263        GLint slot = hooks->gl.glGetAttribLocation(program, name);
264        assert(slot >= 0);
265        switch (type) {
266        case GL_FLOAT:
267        case GL_FLOAT_VEC2:
268        case GL_FLOAT_VEC3:
269        case GL_FLOAT_VEC4:
270            slot += size;
271            break;
272        case GL_FLOAT_MAT2:
273            slot += size * 2;
274            break;
275        case GL_FLOAT_MAT3:
276            slot += size * 3;
277            break;
278        case GL_FLOAT_MAT4:
279            slot += size * 4;
280            break;
281        default:
282            assert(0);
283        }
284        if (slot > maxAttrib)
285            maxAttrib = slot;
286    }
287    delete name;
288    while (GLenum error = hooks->gl.glGetError())
289        LOGD("DbgContext::glUseProgram(%u): after glGetError() = 0x%.4X",
290             program, error);
291}
292
293static bool HasNonVBOAttribs(const DbgContext * const ctx)
294{
295    bool need = false;
296    for (unsigned i = 0; !need && i < ctx->maxAttrib; i++)
297        if (ctx->vertexAttribs[i].enabled && ctx->vertexAttribs[i].buffer == 0)
298            need = true;
299    return need;
300}
301
302void DbgContext::glVertexAttribPointer(GLuint indx, GLint size, GLenum type,
303                                       GLboolean normalized, GLsizei stride, const GLvoid* ptr)
304{
305    assert(GL_NO_ERROR == hooks->gl.glGetError());
306    assert(indx < MAX_VERTEX_ATTRIBS);
307    vertexAttribs[indx].size = size;
308    vertexAttribs[indx].type = type;
309    vertexAttribs[indx].normalized = normalized;
310    switch (type) {
311    case GL_FLOAT:
312        vertexAttribs[indx].elemSize = sizeof(GLfloat) * size;
313        break;
314    case GL_INT:
315    case GL_UNSIGNED_INT:
316        vertexAttribs[indx].elemSize = sizeof(GLint) * size;
317        break;
318    case GL_SHORT:
319    case GL_UNSIGNED_SHORT:
320        vertexAttribs[indx].elemSize = sizeof(GLshort) * size;
321        break;
322    case GL_BYTE:
323    case GL_UNSIGNED_BYTE:
324        vertexAttribs[indx].elemSize = sizeof(GLbyte) * size;
325        break;
326    default:
327        assert(0);
328    }
329    if (0 == stride)
330        stride = vertexAttribs[indx].elemSize;
331    vertexAttribs[indx].stride = stride;
332    vertexAttribs[indx].ptr = ptr;
333    hooks->gl.glGetVertexAttribiv(indx, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
334                                  (GLint *)&vertexAttribs[indx].buffer);
335    hasNonVBOAttribs = HasNonVBOAttribs(this);
336}
337
338void DbgContext::glEnableVertexAttribArray(GLuint index)
339{
340    if (index >= MAX_VERTEX_ATTRIBS)
341        return;
342    vertexAttribs[index].enabled = true;
343    hasNonVBOAttribs = HasNonVBOAttribs(this);
344}
345
346void DbgContext::glDisableVertexAttribArray(GLuint index)
347{
348    if (index >= MAX_VERTEX_ATTRIBS)
349        return;
350    vertexAttribs[index].enabled = false;
351    hasNonVBOAttribs = HasNonVBOAttribs(this);
352}
353
354void DbgContext::glBindBuffer(GLenum target, GLuint buffer)
355{
356    if (GL_ELEMENT_ARRAY_BUFFER != target)
357        return;
358    if (0 == buffer) {
359        indexBuffer = NULL;
360        return;
361    }
362    VBO * b = indexBuffers;
363    indexBuffer = NULL;
364    while (b) {
365        if (b->name == buffer) {
366            assert(GL_ELEMENT_ARRAY_BUFFER == b->target);
367            indexBuffer = b;
368            break;
369        }
370        b = b->next;
371    }
372    if (!indexBuffer)
373        indexBuffer = indexBuffers = new VBO(buffer, target, indexBuffers);
374}
375
376void DbgContext::glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
377{
378    if (GL_ELEMENT_ARRAY_BUFFER != target)
379        return;
380    assert(indexBuffer);
381    assert(size >= 0);
382    indexBuffer->size = size;
383    indexBuffer->data = realloc(indexBuffer->data, size);
384    memcpy(indexBuffer->data, data, size);
385}
386
387void DbgContext::glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
388{
389    if (GL_ELEMENT_ARRAY_BUFFER != target)
390        return;
391    assert(indexBuffer);
392    assert(size >= 0);
393    assert(offset >= 0);
394    assert(offset + size <= indexBuffer->size);
395    memcpy((char *)indexBuffer->data + offset, data, size);
396}
397
398void DbgContext::glDeleteBuffers(GLsizei n, const GLuint *buffers)
399{
400    for (unsigned i = 0; i < n; i++) {
401        for (unsigned j = 0; j < MAX_VERTEX_ATTRIBS; j++)
402            if (buffers[i] == vertexAttribs[j].buffer) {
403                vertexAttribs[j].buffer = 0;
404                vertexAttribs[j].enabled = false;
405            }
406        VBO * b = indexBuffers, * previous = NULL;
407        while (b) {
408            if (b->name == buffers[i]) {
409                assert(GL_ELEMENT_ARRAY_BUFFER == b->target);
410                if (indexBuffer == b)
411                    indexBuffer = NULL;
412                if (previous)
413                    previous->next = b->next;
414                else
415                    indexBuffers = b->next;
416                free(b->data);
417                delete b;
418                break;
419            }
420            previous = b;
421            b = b->next;
422        }
423    }
424    hasNonVBOAttribs = HasNonVBOAttribs(this);
425}
426
427}; // namespace android
428