blob: 41061e1bc6624bd3cc44ccc193c5cac3a015a882 [file] [log] [blame]
David Li5c425f22011-03-10 16:40:37 -08001/*
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 */
David Li6a5ca482011-03-21 10:02:30 -070016
David Li5c425f22011-03-10 16:40:37 -080017#include "header.h"
18
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070019extern "C" {
David Li6a5ca482011-03-21 10:02:30 -070020#include "liblzf/lzf.h"
21}
22
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070023namespace android {
David Li5c425f22011-03-10 16:40:37 -080024
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070025static pthread_key_t dbgEGLThreadLocalStorageKey = -1;
26static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER;
David Lice30eb82011-03-28 10:39:28 -070027
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070028DbgContext * getDbgContextThreadSpecific() {
29 return (DbgContext*)pthread_getspecific(dbgEGLThreadLocalStorageKey);
David Lice30eb82011-03-28 10:39:28 -070030}
31
David Li5c425f22011-03-10 16:40:37 -080032DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
Mathias Agopian575e01c62011-09-13 18:15:50 -070033 const unsigned MAX_VERTEX_ATTRIBS)
David Licbe4e5e2011-03-22 18:48:31 -070034 : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0)
David Li6a5ca482011-03-21 10:02:30 -070035 , version(version), hooks(hooks)
David Li5c425f22011-03-10 16:40:37 -080036 , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
Mathias Agopian575e01c62011-09-13 18:15:50 -070037 , readBytesPerPixel(4)
David Lie7180e82011-04-08 18:45:45 -070038 , captureSwap(0), captureDraw(0)
David Li5c425f22011-03-10 16:40:37 -080039 , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
40 , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
David Lifbfc7032011-03-21 16:25:58 -070041 , program(0), maxAttrib(0)
David Li5c425f22011-03-10 16:40:37 -080042{
David Lif9bc1242011-03-22 18:42:03 -070043 lzf_ref[0] = lzf_ref[1] = NULL;
David Li5c425f22011-03-10 16:40:37 -080044 for (unsigned i = 0; i < MAX_VERTEX_ATTRIBS; i++)
45 vertexAttribs[i] = VertexAttrib();
David Lifbfc7032011-03-21 16:25:58 -070046 memset(&expectResponse, 0, sizeof(expectResponse));
David Li5c425f22011-03-10 16:40:37 -080047}
48
49DbgContext::~DbgContext()
50{
51 delete vertexAttribs;
David Li6a5ca482011-03-21 10:02:30 -070052 free(lzf_buf);
David Lif9bc1242011-03-22 18:42:03 -070053 free(lzf_ref[0]);
54 free(lzf_ref[1]);
David Li5c425f22011-03-10 16:40:37 -080055}
56
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070057DbgContext* CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks)
David Li5c425f22011-03-10 16:40:37 -080058{
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070059 pthread_mutex_lock(&gThreadLocalStorageKeyMutex);
60 if (dbgEGLThreadLocalStorageKey == -1)
61 pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
62 pthread_mutex_unlock(&gThreadLocalStorageKeyMutex);
63
David Li5c425f22011-03-10 16:40:37 -080064 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);
Mathias Agopian575e01c62011-09-13 18:15:50 -070068 DbgContext* dbg = new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
David Li31918cc2011-04-15 15:35:10 -070069 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);
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070084
Mathias Agopian42220922011-09-01 17:50:52 -070085 pthread_setspecific(dbgEGLThreadLocalStorageKey, dbg);
David Li31918cc2011-04-15 15:35:10 -070086 return dbg;
David Li5c425f22011-03-10 16:40:37 -080087}
88
Mathias Agopian7adf4ef2011-05-13 16:21:08 -070089void dbgReleaseThread() {
90 delete getDbgContextThreadSpecific();
David Li5c425f22011-03-10 16:40:37 -080091}
92
David Lifbfc7032011-03-21 16:25:58 -070093unsigned GetBytesPerPixel(const GLenum format, const GLenum type)
94{
95 switch (type) {
96 case GL_UNSIGNED_SHORT_5_6_5:
David Lifbfc7032011-03-21 16:25:58 -070097 case GL_UNSIGNED_SHORT_4_4_4_4:
David Lifbfc7032011-03-21 16:25:58 -070098 case GL_UNSIGNED_SHORT_5_5_5_1:
99 return 2;
100 case GL_UNSIGNED_BYTE:
101 break;
102 default:
Mathias Agopian575e01c62011-09-13 18:15:50 -0700103 LOGE("GetBytesPerPixel: unknown type %x", type);
David Lifbfc7032011-03-21 16:25:58 -0700104 }
105
106 switch (format) {
107 case GL_ALPHA:
David Lifbfc7032011-03-21 16:25:58 -0700108 case GL_LUMINANCE:
109 return 1;
David Lifbfc7032011-03-21 16:25:58 -0700110 case GL_LUMINANCE_ALPHA:
111 return 2;
112 case GL_RGB:
113 return 3;
114 case GL_RGBA:
Mathias Agopian575e01c62011-09-13 18:15:50 -0700115 case 0x80E1: // GL_BGRA_EXT
David Lifbfc7032011-03-21 16:25:58 -0700116 return 4;
117 default:
Mathias Agopian575e01c62011-09-13 18:15:50 -0700118 LOGE("GetBytesPerPixel: unknown format %x", format);
David Lifbfc7032011-03-21 16:25:58 -0700119 }
Mathias Agopian575e01c62011-09-13 18:15:50 -0700120
121 return 1; // in doubt...
David Lifbfc7032011-03-21 16:25:58 -0700122}
123
David Li5c425f22011-03-10 16:40:37 -0800124void 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
David Licbe4e5e2011-03-22 18:48:31 -0700138void DbgContext::Compress(const void * in_data, unsigned int in_len,
139 std::string * const outStr)
David Li6a5ca482011-03-21 10:02:30 -0700140{
David Licbe4e5e2011-03-22 18:48:31 -0700141 if (!lzf_buf)
142 lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
David Lie2ad4d02011-04-08 18:41:00 -0700143 assert(lzf_buf);
David Licbe4e5e2011-03-22 18:48:31 -0700144 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);
David Li6a5ca482011-03-21 10:02:30 -0700158 }
David Li6a5ca482011-03-21 10:02:30 -0700159}
160
David Lie9f619f2011-04-14 11:20:22 -0700161unsigned 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
David Lif9bc1242011-03-22 18:42:03 -0700192void * DbgContext::GetReadPixelsBuffer(const unsigned size)
193{
David Lifbfc7032011-03-21 16:25:58 -0700194 if (lzf_refBufSize < size + 8) {
195 lzf_refBufSize = size + 8;
David Lif9bc1242011-03-22 18:42:03 -0700196 lzf_ref[0] = (unsigned *)realloc(lzf_ref[0], lzf_refBufSize);
David Lie2ad4d02011-04-08 18:41:00 -0700197 assert(lzf_ref[0]);
David Lifbfc7032011-03-21 16:25:58 -0700198 memset(lzf_ref[0], 0, lzf_refBufSize);
David Lif9bc1242011-03-22 18:42:03 -0700199 lzf_ref[1] = (unsigned *)realloc(lzf_ref[1], lzf_refBufSize);
David Lie2ad4d02011-04-08 18:41:00 -0700200 assert(lzf_ref[1]);
David Lifbfc7032011-03-21 16:25:58 -0700201 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);
David Lif9bc1242011-03-22 18:42:03 -0700207 }
208 lzf_refSize = size;
209 lzf_readIndex ^= 1;
210 return lzf_ref[lzf_readIndex];
211}
212
David Licbe4e5e2011-03-22 18:48:31 -0700213void DbgContext::CompressReadPixelBuffer(std::string * const outStr)
David Lif9bc1242011-03-22 18:42:03 -0700214{
David Lie2ad4d02011-04-08 18:41:00 -0700215 assert(lzf_ref[0] && lzf_ref[1]);
David Lif9bc1242011-03-22 18:42:03 -0700216 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];
David Licbe4e5e2011-03-22 18:48:31 -0700220 Compress(ref, lzf_refSize, outStr);
David Lif9bc1242011-03-22 18:42:03 -0700221}
222
David Lie2ad4d02011-04-08 18:41:00 -0700223char * 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
David Li5c425f22011-03-10 16:40:37 -0800242void DbgContext::glUseProgram(GLuint program)
243{
David Li940c3f82011-03-10 19:07:42 -0800244 while (GLenum error = hooks->gl.glGetError())
David Li27f130a2011-04-08 18:43:16 -0700245 LOGD("DbgContext::glUseProgram(%u): before glGetError() = 0x%.4X",
246 program, error);
David Li5c425f22011-03-10 16:40:37 -0800247 this->program = program;
David Li27f130a2011-04-08 18:43:16 -0700248 maxAttrib = 0;
249 if (program == 0)
250 return;
David Li5c425f22011-03-10 16:40:37 -0800251 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;
David Li940c3f82011-03-10 19:07:42 -0800288 while (GLenum error = hooks->gl.glGetError())
David Li27f130a2011-04-08 18:43:16 -0700289 LOGD("DbgContext::glUseProgram(%u): after glGetError() = 0x%.4X",
290 program, error);
David Li5c425f22011-03-10 16:40:37 -0800291}
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{
David Li27f130a2011-04-08 18:43:16 -0700340 if (index >= MAX_VERTEX_ATTRIBS)
341 return;
David Li5c425f22011-03-10 16:40:37 -0800342 vertexAttribs[index].enabled = true;
343 hasNonVBOAttribs = HasNonVBOAttribs(this);
344}
345
346void DbgContext::glDisableVertexAttribArray(GLuint index)
347{
David Li27f130a2011-04-08 18:43:16 -0700348 if (index >= MAX_VERTEX_ATTRIBS)
349 return;
David Li5c425f22011-03-10 16:40:37 -0800350 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