/* ** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include "header.h" static inline void GetFormatAndBytesPerPixel(const GLenum format, unsigned & bytesPerPixel) { switch (format) { case GL_ALPHA: bytesPerPixel = 1; break; case GL_LUMINANCE: bytesPerPixel = 1; break; case GL_LUMINANCE_ALPHA: bytesPerPixel = 2; break; case GL_RGB: bytesPerPixel = 3; break; case GL_RGBA: bytesPerPixel = 4; break; // internal formats to avoid conversion case GL_UNSIGNED_SHORT_5_6_5: bytesPerPixel = 2; break; case GL_UNSIGNED_SHORT_4_4_4_4: bytesPerPixel = 2; break; case GL_UNSIGNED_SHORT_5_5_5_1: bytesPerPixel = 2; break; default: assert(0); return; } } #define USE_RLE 0 #if USE_RLE template void * RLEEncode(const void * pixels, unsigned count, unsigned * encodedSize) { // first is a byte indicating data size [1,2,4] bytes // then an unsigned indicating decompressed size // then a byte of header: MSB == 1 indicates run, else literal // LSB7 is run or literal length (actual length - 1) const T * data = (T *)pixels; unsigned bufferSize = sizeof(T) * count / 2 + 8; unsigned char * buffer = (unsigned char *)malloc(bufferSize); buffer[0] = sizeof(T); unsigned bufferWritten = 1; // number of bytes written *(unsigned *)(buffer + bufferWritten) = count; bufferWritten += sizeof(count); while (count) { unsigned char run = 1; bool repeat = true; for (run = 1; run < count; run++) if (data[0] != data[run]) { repeat = false; break; } else if (run > 127) break; if (!repeat) { // find literal length for (run = 1; run < count; run++) if (data[run - 1] == data[run]) break; else if (run > 127) break; unsigned bytesToWrite = 1 + sizeof(T) * run; if (bufferWritten + bytesToWrite > bufferSize) { bufferSize += sizeof(T) * run + 256; buffer = (unsigned char *)realloc(buffer, bufferSize); } buffer[bufferWritten++] = run - 1; for (unsigned i = 0; i < run; i++) { *(T *)(buffer + bufferWritten) = *data; bufferWritten += sizeof(T); data++; } count -= run; } else { unsigned bytesToWrite = 1 + sizeof(T); if (bufferWritten + bytesToWrite > bufferSize) { bufferSize += 256; buffer = (unsigned char *)realloc(buffer, bufferSize); } buffer[bufferWritten++] = (run - 1) | 0x80; *(T *)(buffer + bufferWritten) = data[0]; bufferWritten += sizeof(T); data += run; count -= run; } } if (encodedSize) *encodedSize = bufferWritten; return buffer; } void * RLEEncode(const void * pixels, const unsigned bytesPerPixel, const unsigned count, unsigned * encodedSize) { switch (bytesPerPixel) { case 4: return RLEEncode(pixels, count, encodedSize); case 2: return RLEEncode(pixels, count, encodedSize); case 1: return RLEEncode(pixels, count, encodedSize); default: assert(0); return NULL; } } #endif void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * pixels) { // LOGD("\n*\n* GLESv2_dbg: %s \n*", "glTexImage2D"); gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; GLESv2Debugger::Message msg, cmd; msg.set_context_id(0); msg.set_has_next_message(true); const bool expectResponse = false; msg.set_expect_response(expectResponse); msg.set_function(GLESv2Debugger::Message_Function_glTexImage2D); msg.set_arg0(target); msg.set_arg1(level); msg.set_arg2(internalformat); msg.set_arg3(width); msg.set_arg4(height); msg.set_arg5(border); msg.set_arg6(format); msg.set_arg7(type); if (pixels) { assert(internalformat == format); assert(0 == border); GLenum newFormat = internalformat; switch (type) { case GL_UNSIGNED_BYTE: break; case GL_UNSIGNED_SHORT_5_6_5: case GL_UNSIGNED_SHORT_4_4_4_4: case GL_UNSIGNED_SHORT_5_5_5_1: newFormat = type; break; default: LOGD("GLESv2_dbg: glTexImage2D type=0x%.4X", type); assert(0); } unsigned bytesPerPixel = 0; GetFormatAndBytesPerPixel(newFormat, bytesPerPixel); assert(0 < bytesPerPixel); // LOGD("GLESv2_dbg: glTexImage2D width=%d height=%d level=%d bytesPerPixel=%d", // width, height, level, bytesPerPixel); #if USE_RLE unsigned encodedSize = 0; void * data = RLEEncode(pixels, bytesPerPixel, width * height, &encodedSize); msg.set_data(data, encodedSize); free(data); if (encodedSize > bytesPerPixel * width * height) LOGD("GLESv2_dbg: glTexImage2D sending data encodedSize=%d size=%d", encodedSize, bytesPerPixel * width * height); #else msg.set_data(pixels, bytesPerPixel * width * height); #endif } assert(msg.has_arg3()); Send(msg, cmd); if (!expectResponse) cmd.set_function(GLESv2Debugger::Message_Function_CONTINUE); while (true) { msg.Clear(); clock_t c0 = clock(); switch (cmd.function()) { case GLESv2Debugger::Message_Function_CONTINUE: _c->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); msg.set_time((float(clock()) - c0) / CLOCKS_PER_SEC); msg.set_context_id(0); msg.set_function(GLESv2Debugger::Message_Function_glTexImage2D); msg.set_has_next_message(false); msg.set_expect_response(expectResponse); assert(!msg.has_arg3()); Send(msg, cmd); if (!expectResponse) cmd.set_function(GLESv2Debugger::Message_Function_SKIP); break; case GLESv2Debugger::Message_Function_SKIP: return; default: ASSERT(0); //GenerateCall(msg, cmd); break; } } } void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) { // LOGD("\n*\n* GLESv2_dbg: %s \n*", "glTexSubImage2D"); gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; GLESv2Debugger::Message msg, cmd; msg.set_context_id(0); msg.set_has_next_message(true); const bool expectResponse = false; msg.set_expect_response(expectResponse); msg.set_function(GLESv2Debugger::Message_Function_glTexSubImage2D); msg.set_arg0(target); msg.set_arg1(level); msg.set_arg2(xoffset); msg.set_arg3(yoffset); msg.set_arg4(width); msg.set_arg5(height); msg.set_arg6(format); msg.set_arg7(type); assert(pixels); if (pixels) { GLenum newFormat = format; switch (type) { case GL_UNSIGNED_BYTE: break; case GL_UNSIGNED_SHORT_5_6_5: case GL_UNSIGNED_SHORT_4_4_4_4: case GL_UNSIGNED_SHORT_5_5_5_1: newFormat = type; break; default: assert(0); } unsigned bytesPerPixel = 0; GetFormatAndBytesPerPixel(newFormat, bytesPerPixel); assert(0 < bytesPerPixel); // LOGD("GLESv2_dbg: glTexSubImage2D width=%d height=%d level=%d bytesPerPixel=%d", // width, height, level, bytesPerPixel); #if USE_RLE unsigned encodedSize = 0; void * data = RLEEncode(pixels, bytesPerPixel, width * height, &encodedSize); msg.set_data(data, encodedSize); free(data); if (encodedSize > bytesPerPixel * width * height) LOGD("GLESv2_dbg: glTexImage2D sending data encodedSize=%d size=%d", encodedSize, bytesPerPixel * width * height); #else msg.set_data(pixels, bytesPerPixel * width * height); #endif } Send(msg, cmd); if (!expectResponse) cmd.set_function(GLESv2Debugger::Message_Function_CONTINUE); while (true) { msg.Clear(); clock_t c0 = clock(); switch (cmd.function()) { case GLESv2Debugger::Message_Function_CONTINUE: _c->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); msg.set_time((float(clock()) - c0) / CLOCKS_PER_SEC); msg.set_function(GLESv2Debugger::Message_Function_glTexImage2D); msg.set_context_id(0); msg.set_has_next_message(false); msg.set_expect_response(expectResponse); Send(msg, cmd); if (!expectResponse) cmd.set_function(GLESv2Debugger::Message_Function_SKIP); break; case GLESv2Debugger::Message_Function_SKIP: return; default: ASSERT(0); //GenerateCall(msg, cmd); break; } } }