1ac8b72f4f
always use GL_RGBA, GL_UNSIGNED_BYTE for screen capture and make sure to handle GL_BGRA_EXT used on some gpu. Change-Id: If9c973677fec8a5c4e72be22e7ef7d4bf5f008f4
469 lines
15 KiB
C++
469 lines
15 KiB
C++
/*
|
|
** 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 <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "header.h"
|
|
#include "gtest/gtest.h"
|
|
#include "hooks.h"
|
|
|
|
namespace android
|
|
{
|
|
extern int serverSock, clientSock;
|
|
};
|
|
|
|
void * glNoop();
|
|
|
|
class SocketContextTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
DbgContext* dbg;
|
|
gl_hooks_t hooks;
|
|
int sock;
|
|
char * buffer;
|
|
unsigned int bufferSize;
|
|
|
|
SocketContextTest() : sock(-1) {
|
|
}
|
|
|
|
virtual ~SocketContextTest() {
|
|
}
|
|
|
|
virtual void SetUp() {
|
|
dbg = new DbgContext(1, &hooks, 32);
|
|
ASSERT_TRUE(dbg != NULL);
|
|
for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
|
|
((void **)&hooks)[i] = (void *)glNoop;
|
|
|
|
int socks[2] = {-1, -1};
|
|
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socks));
|
|
clientSock = socks[0];
|
|
sock = socks[1];
|
|
|
|
bufferSize = 128;
|
|
buffer = new char [128];
|
|
ASSERT_NE((char *)NULL, buffer);
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
close(sock);
|
|
close(clientSock);
|
|
clientSock = -1;
|
|
delete buffer;
|
|
}
|
|
|
|
void Write(glesv2debugger::Message & msg) const {
|
|
msg.set_context_id((int)dbg);
|
|
msg.set_type(msg.Response);
|
|
ASSERT_TRUE(msg.has_context_id());
|
|
ASSERT_TRUE(msg.has_function());
|
|
ASSERT_TRUE(msg.has_type());
|
|
ASSERT_TRUE(msg.has_expect_response());
|
|
static std::string str;
|
|
msg.SerializeToString(&str);
|
|
const uint32_t len = str.length();
|
|
ASSERT_EQ(sizeof(len), send(sock, &len, sizeof(len), 0));
|
|
ASSERT_EQ(str.length(), send(sock, str.data(), str.length(), 0));
|
|
}
|
|
|
|
void Read(glesv2debugger::Message & msg) {
|
|
int available = 0;
|
|
ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
|
|
ASSERT_GT(available, 0);
|
|
uint32_t len = 0;
|
|
ASSERT_EQ(sizeof(len), recv(sock, &len, sizeof(len), 0));
|
|
if (len > bufferSize) {
|
|
bufferSize = len;
|
|
buffer = new char[bufferSize];
|
|
ASSERT_TRUE(buffer != NULL);
|
|
}
|
|
ASSERT_EQ(len, recv(sock, buffer, len, 0));
|
|
msg.Clear();
|
|
msg.ParseFromArray(buffer, len);
|
|
ASSERT_TRUE(msg.has_context_id());
|
|
ASSERT_TRUE(msg.has_function());
|
|
ASSERT_TRUE(msg.has_type());
|
|
ASSERT_TRUE(msg.has_expect_response());
|
|
}
|
|
|
|
void CheckNoAvailable() {
|
|
int available = 0;
|
|
ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
|
|
ASSERT_EQ(available, 0);
|
|
}
|
|
};
|
|
|
|
TEST_F(SocketContextTest, MessageLoopSkip)
|
|
{
|
|
static const int arg0 = 45;
|
|
static const float arg7 = -87.2331f;
|
|
static const int arg8 = -3;
|
|
static const int * ret = (int *)870;
|
|
|
|
struct Caller : public FunctionCall {
|
|
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
|
|
msg.set_arg0(arg0);
|
|
msg.set_arg7((int &)arg7);
|
|
msg.set_arg8(arg8);
|
|
return ret;
|
|
}
|
|
} caller;
|
|
glesv2debugger::Message msg, read, cmd;
|
|
dbg->expectResponse.Bit(msg.glFinish, true);
|
|
|
|
cmd.set_function(cmd.SKIP);
|
|
cmd.set_expect_response(false);
|
|
Write(cmd);
|
|
|
|
EXPECT_NE(ret, MessageLoop(caller, msg, msg.glFinish));
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glFinish, read.function());
|
|
EXPECT_EQ(read.BeforeCall, read.type());
|
|
EXPECT_NE(arg0, read.arg0());
|
|
EXPECT_NE((int &)arg7, read.arg7());
|
|
EXPECT_NE(arg8, read.arg8());
|
|
|
|
CheckNoAvailable();
|
|
}
|
|
|
|
TEST_F(SocketContextTest, MessageLoopContinue)
|
|
{
|
|
static const int arg0 = GL_FRAGMENT_SHADER;
|
|
static const int ret = -342;
|
|
struct Caller : public FunctionCall {
|
|
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
|
|
msg.set_ret(ret);
|
|
return (int *)ret;
|
|
}
|
|
} caller;
|
|
glesv2debugger::Message msg, read, cmd;
|
|
dbg->expectResponse.Bit(msg.glCreateShader, true);
|
|
|
|
cmd.set_function(cmd.CONTINUE);
|
|
cmd.set_expect_response(false); // MessageLoop should automatically skip after continue
|
|
Write(cmd);
|
|
|
|
msg.set_arg0(arg0);
|
|
EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateShader));
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateShader, read.function());
|
|
EXPECT_EQ(read.BeforeCall, read.type());
|
|
EXPECT_EQ(arg0, read.arg0());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateShader, read.function());
|
|
EXPECT_EQ(read.AfterCall, read.type());
|
|
EXPECT_EQ(ret, read.ret());
|
|
|
|
CheckNoAvailable();
|
|
}
|
|
|
|
TEST_F(SocketContextTest, MessageLoopGenerateCall)
|
|
{
|
|
static const int ret = -342;
|
|
static unsigned int createShader, createProgram;
|
|
createShader = 0;
|
|
createProgram = 0;
|
|
struct Caller : public FunctionCall {
|
|
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
|
|
const int r = (int)_c->glCreateProgram();
|
|
msg.set_ret(r);
|
|
return (int *)r;
|
|
}
|
|
static GLuint CreateShader(const GLenum type) {
|
|
createShader++;
|
|
return type;
|
|
}
|
|
static GLuint CreateProgram() {
|
|
createProgram++;
|
|
return ret;
|
|
}
|
|
} caller;
|
|
glesv2debugger::Message msg, read, cmd;
|
|
hooks.gl.glCreateShader = caller.CreateShader;
|
|
hooks.gl.glCreateProgram = caller.CreateProgram;
|
|
dbg->expectResponse.Bit(msg.glCreateProgram, true);
|
|
|
|
cmd.set_function(cmd.glCreateShader);
|
|
cmd.set_arg0(GL_FRAGMENT_SHADER);
|
|
cmd.set_expect_response(true);
|
|
Write(cmd);
|
|
|
|
cmd.Clear();
|
|
cmd.set_function(cmd.CONTINUE);
|
|
cmd.set_expect_response(true);
|
|
Write(cmd);
|
|
|
|
cmd.set_function(cmd.glCreateShader);
|
|
cmd.set_arg0(GL_VERTEX_SHADER);
|
|
cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
|
|
Write(cmd);
|
|
|
|
EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateProgram, read.function());
|
|
EXPECT_EQ(read.BeforeCall, read.type());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateShader, read.function());
|
|
EXPECT_EQ(read.AfterGeneratedCall, read.type());
|
|
EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateProgram, read.function());
|
|
EXPECT_EQ(read.AfterCall, read.type());
|
|
EXPECT_EQ(ret, read.ret());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateShader, read.function());
|
|
EXPECT_EQ(read.AfterGeneratedCall, read.type());
|
|
EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
|
|
|
|
EXPECT_EQ(2, createShader);
|
|
EXPECT_EQ(1, createProgram);
|
|
|
|
CheckNoAvailable();
|
|
}
|
|
|
|
TEST_F(SocketContextTest, MessageLoopSetProp)
|
|
{
|
|
static const int ret = -342;
|
|
static unsigned int createShader, createProgram;
|
|
createShader = 0;
|
|
createProgram = 0;
|
|
struct Caller : public FunctionCall {
|
|
const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
|
|
const int r = (int)_c->glCreateProgram();
|
|
msg.set_ret(r);
|
|
return (int *)r;
|
|
}
|
|
static GLuint CreateShader(const GLenum type) {
|
|
createShader++;
|
|
return type;
|
|
}
|
|
static GLuint CreateProgram() {
|
|
createProgram++;
|
|
return ret;
|
|
}
|
|
} caller;
|
|
glesv2debugger::Message msg, read, cmd;
|
|
hooks.gl.glCreateShader = caller.CreateShader;
|
|
hooks.gl.glCreateProgram = caller.CreateProgram;
|
|
dbg->expectResponse.Bit(msg.glCreateProgram, false);
|
|
|
|
cmd.set_function(cmd.SETPROP);
|
|
cmd.set_prop(cmd.ExpectResponse);
|
|
cmd.set_arg0(cmd.glCreateProgram);
|
|
cmd.set_arg1(true);
|
|
cmd.set_expect_response(true);
|
|
Write(cmd);
|
|
|
|
cmd.Clear();
|
|
cmd.set_function(cmd.glCreateShader);
|
|
cmd.set_arg0(GL_FRAGMENT_SHADER);
|
|
cmd.set_expect_response(true);
|
|
Write(cmd);
|
|
|
|
cmd.set_function(cmd.SETPROP);
|
|
cmd.set_prop(cmd.CaptureDraw);
|
|
cmd.set_arg0(819);
|
|
cmd.set_expect_response(true);
|
|
Write(cmd);
|
|
|
|
cmd.Clear();
|
|
cmd.set_function(cmd.CONTINUE);
|
|
cmd.set_expect_response(true);
|
|
Write(cmd);
|
|
|
|
cmd.set_function(cmd.glCreateShader);
|
|
cmd.set_arg0(GL_VERTEX_SHADER);
|
|
cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
|
|
Write(cmd);
|
|
|
|
EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
|
|
|
|
EXPECT_TRUE(dbg->expectResponse.Bit(msg.glCreateProgram));
|
|
EXPECT_EQ(819, dbg->captureDraw);
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateProgram, read.function());
|
|
EXPECT_EQ(read.BeforeCall, read.type());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateShader, read.function());
|
|
EXPECT_EQ(read.AfterGeneratedCall, read.type());
|
|
EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateProgram, read.function());
|
|
EXPECT_EQ(read.AfterCall, read.type());
|
|
EXPECT_EQ(ret, read.ret());
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCreateShader, read.function());
|
|
EXPECT_EQ(read.AfterGeneratedCall, read.type());
|
|
EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
|
|
|
|
EXPECT_EQ(2, createShader);
|
|
EXPECT_EQ(1, createProgram);
|
|
|
|
CheckNoAvailable();
|
|
}
|
|
|
|
TEST_F(SocketContextTest, TexImage2D)
|
|
{
|
|
static const GLenum _target = GL_TEXTURE_2D;
|
|
static const GLint _level = 1, _internalformat = GL_RGBA;
|
|
static const GLsizei _width = 2, _height = 2;
|
|
static const GLint _border = 333;
|
|
static const GLenum _format = GL_RGB, _type = GL_UNSIGNED_SHORT_5_6_5;
|
|
static const short _pixels [_width * _height] = {11, 22, 33, 44};
|
|
static unsigned int texImage2D;
|
|
texImage2D = 0;
|
|
|
|
struct Caller {
|
|
static void TexImage2D(GLenum target, GLint level, GLint internalformat,
|
|
GLsizei width, GLsizei height, GLint border,
|
|
GLenum format, GLenum type, const GLvoid* pixels) {
|
|
EXPECT_EQ(_target, target);
|
|
EXPECT_EQ(_level, level);
|
|
EXPECT_EQ(_internalformat, internalformat);
|
|
EXPECT_EQ(_width, width);
|
|
EXPECT_EQ(_height, height);
|
|
EXPECT_EQ(_border, border);
|
|
EXPECT_EQ(_format, format);
|
|
EXPECT_EQ(_type, type);
|
|
EXPECT_EQ(0, memcmp(_pixels, pixels, sizeof(_pixels)));
|
|
texImage2D++;
|
|
}
|
|
} caller;
|
|
glesv2debugger::Message msg, read, cmd;
|
|
hooks.gl.glTexImage2D = caller.TexImage2D;
|
|
dbg->expectResponse.Bit(msg.glTexImage2D, false);
|
|
|
|
Debug_glTexImage2D(_target, _level, _internalformat, _width, _height, _border,
|
|
_format, _type, _pixels);
|
|
EXPECT_EQ(1, texImage2D);
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glTexImage2D, read.function());
|
|
EXPECT_EQ(read.BeforeCall, read.type());
|
|
EXPECT_EQ(_target, read.arg0());
|
|
EXPECT_EQ(_level, read.arg1());
|
|
EXPECT_EQ(_internalformat, read.arg2());
|
|
EXPECT_EQ(_width, read.arg3());
|
|
EXPECT_EQ(_height, read.arg4());
|
|
EXPECT_EQ(_border, read.arg5());
|
|
EXPECT_EQ(_format, read.arg6());
|
|
EXPECT_EQ(_type, read.arg7());
|
|
|
|
EXPECT_TRUE(read.has_data());
|
|
uint32_t dataLen = 0;
|
|
const unsigned char * data = dbg->Decompress(read.data().data(),
|
|
read.data().length(), &dataLen);
|
|
EXPECT_EQ(sizeof(_pixels), dataLen);
|
|
if (sizeof(_pixels) == dataLen)
|
|
EXPECT_EQ(0, memcmp(_pixels, data, sizeof(_pixels)));
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glTexImage2D, read.function());
|
|
EXPECT_EQ(read.AfterCall, read.type());
|
|
|
|
CheckNoAvailable();
|
|
}
|
|
|
|
TEST_F(SocketContextTest, CopyTexImage2D)
|
|
{
|
|
static const GLenum _target = GL_TEXTURE_2D;
|
|
static const GLint _level = 1, _internalformat = GL_RGBA;
|
|
static const GLint _x = 9, _y = 99;
|
|
static const GLsizei _width = 2, _height = 3;
|
|
static const GLint _border = 333;
|
|
static const int _pixels [_width * _height] = {11, 22, 33, 44, 55, 66};
|
|
static unsigned int copyTexImage2D, readPixels;
|
|
copyTexImage2D = 0, readPixels = 0;
|
|
|
|
struct Caller {
|
|
static void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
|
|
GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
|
|
EXPECT_EQ(_target, target);
|
|
EXPECT_EQ(_level, level);
|
|
EXPECT_EQ(_internalformat, internalformat);
|
|
EXPECT_EQ(_x, x);
|
|
EXPECT_EQ(_y, y);
|
|
EXPECT_EQ(_width, width);
|
|
EXPECT_EQ(_height, height);
|
|
EXPECT_EQ(_border, border);
|
|
copyTexImage2D++;
|
|
}
|
|
static void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
|
|
GLenum format, GLenum type, GLvoid* pixels) {
|
|
EXPECT_EQ(_x, x);
|
|
EXPECT_EQ(_y, y);
|
|
EXPECT_EQ(_width, width);
|
|
EXPECT_EQ(_height, height);
|
|
EXPECT_EQ(GL_RGBA, format);
|
|
EXPECT_EQ(GL_UNSIGNED_BYTE, type);
|
|
ASSERT_TRUE(pixels != NULL);
|
|
memcpy(pixels, _pixels, sizeof(_pixels));
|
|
readPixels++;
|
|
}
|
|
} caller;
|
|
glesv2debugger::Message msg, read, cmd;
|
|
hooks.gl.glCopyTexImage2D = caller.CopyTexImage2D;
|
|
hooks.gl.glReadPixels = caller.ReadPixels;
|
|
dbg->expectResponse.Bit(msg.glCopyTexImage2D, false);
|
|
|
|
Debug_glCopyTexImage2D(_target, _level, _internalformat, _x, _y, _width, _height,
|
|
_border);
|
|
ASSERT_EQ(1, copyTexImage2D);
|
|
ASSERT_EQ(1, readPixels);
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCopyTexImage2D, read.function());
|
|
EXPECT_EQ(read.BeforeCall, read.type());
|
|
EXPECT_EQ(_target, read.arg0());
|
|
EXPECT_EQ(_level, read.arg1());
|
|
EXPECT_EQ(_internalformat, read.arg2());
|
|
EXPECT_EQ(_x, read.arg3());
|
|
EXPECT_EQ(_y, read.arg4());
|
|
EXPECT_EQ(_width, read.arg5());
|
|
EXPECT_EQ(_height, read.arg6());
|
|
EXPECT_EQ(_border, read.arg7());
|
|
|
|
EXPECT_TRUE(read.has_data());
|
|
EXPECT_EQ(read.ReferencedImage, read.data_type());
|
|
EXPECT_EQ(GL_RGBA, read.pixel_format());
|
|
EXPECT_EQ(GL_UNSIGNED_BYTE, read.pixel_type());
|
|
uint32_t dataLen = 0;
|
|
unsigned char * const data = dbg->Decompress(read.data().data(),
|
|
read.data().length(), &dataLen);
|
|
ASSERT_EQ(sizeof(_pixels), dataLen);
|
|
for (unsigned i = 0; i < sizeof(_pixels) / sizeof(*_pixels); i++)
|
|
EXPECT_EQ(_pixels[i], ((const int *)data)[i]) << "xor with 0 ref is identity";
|
|
free(data);
|
|
|
|
Read(read);
|
|
EXPECT_EQ(read.glCopyTexImage2D, read.function());
|
|
EXPECT_EQ(read.AfterCall, read.type());
|
|
|
|
CheckNoAvailable();
|
|
}
|