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
19 extern "C" {
20 #include "liblzf/lzf.h"
21 }
22
23 namespace android {
24
25 static pthread_key_t dbgEGLThreadLocalStorageKey = -1;
26 static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER;
27
getDbgContextThreadSpecific()28 DbgContext * getDbgContextThreadSpecific() {
29 return (DbgContext*)pthread_getspecific(dbgEGLThreadLocalStorageKey);
30 }
31
DbgContext(const unsigned version,const gl_hooks_t * const hooks,const unsigned MAX_VERTEX_ATTRIBS)32 DbgContext::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
~DbgContext()49 DbgContext::~DbgContext()
50 {
51 delete vertexAttribs;
52 free(lzf_buf);
53 free(lzf_ref[0]);
54 free(lzf_ref[1]);
55 }
56
CreateDbgContext(const unsigned version,const gl_hooks_t * const hooks)57 DbgContext* 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
dbgReleaseThread()89 void dbgReleaseThread() {
90 delete getDbgContextThreadSpecific();
91 }
92
GetBytesPerPixel(const GLenum format,const GLenum type)93 unsigned 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
Fetch(const unsigned index,std::string * const data) const124 void 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
Compress(const void * in_data,unsigned int in_len,std::string * const outStr)138 void 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
Decompress(const void * in,const unsigned int inLen,unsigned int * const outLen)161 unsigned 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
GetReadPixelsBuffer(const unsigned size)192 void * 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
CompressReadPixelBuffer(std::string * const outStr)213 void 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
GetBuffer()223 char * 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
GetBufferSize()231 unsigned 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
glUseProgram(GLuint program)242 void 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
HasNonVBOAttribs(const DbgContext * const ctx)293 static 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
glVertexAttribPointer(GLuint indx,GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * ptr)302 void 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
glEnableVertexAttribArray(GLuint index)338 void DbgContext::glEnableVertexAttribArray(GLuint index)
339 {
340 if (index >= MAX_VERTEX_ATTRIBS)
341 return;
342 vertexAttribs[index].enabled = true;
343 hasNonVBOAttribs = HasNonVBOAttribs(this);
344 }
345
glDisableVertexAttribArray(GLuint index)346 void DbgContext::glDisableVertexAttribArray(GLuint index)
347 {
348 if (index >= MAX_VERTEX_ATTRIBS)
349 return;
350 vertexAttribs[index].enabled = false;
351 hasNonVBOAttribs = HasNonVBOAttribs(this);
352 }
353
glBindBuffer(GLenum target,GLuint buffer)354 void 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
glBufferData(GLenum target,GLsizeiptr size,const GLvoid * data,GLenum usage)376 void 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
glBufferSubData(GLenum target,GLintptr offset,GLsizeiptr size,const GLvoid * data)387 void 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
glDeleteBuffers(GLsizei n,const GLuint * buffers)398 void 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