|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "gpu/command_buffer/tests/gl_test_utils.h" | 
|  | #include <string> | 
|  | #include <stdio.h> | 
|  | #include "base/basictypes.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | // GCC requires these declarations, but MSVC requires they not be present. | 
|  | #ifndef COMPILER_MSVC | 
|  | const uint8 GLTestHelper::kCheckClearValue; | 
|  | #endif | 
|  |  | 
|  | bool GLTestHelper::HasExtension(const char* extension) { | 
|  | std::string extensions( | 
|  | reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))); | 
|  | return extensions.find(extension) != std::string::npos; | 
|  | } | 
|  |  | 
|  | bool GLTestHelper::CheckGLError(const char* msg, int line) { | 
|  | bool success = true; | 
|  | GLenum error = GL_NO_ERROR; | 
|  | while ((error = glGetError()) != GL_NO_ERROR) { | 
|  | success = false; | 
|  | EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), error) | 
|  | << "GL ERROR in " << msg << " at line " << line << " : " << error; | 
|  | } | 
|  | return success; | 
|  | } | 
|  |  | 
|  | GLuint GLTestHelper::LoadShader(GLenum type, const char* shaderSrc) { | 
|  | GLuint shader = glCreateShader(type); | 
|  | // Load the shader source | 
|  | glShaderSource(shader, 1, &shaderSrc, NULL); | 
|  | // Compile the shader | 
|  | glCompileShader(shader); | 
|  | // Check the compile status | 
|  | GLint value = 0; | 
|  | glGetShaderiv(shader, GL_COMPILE_STATUS, &value); | 
|  | if (value == 0) { | 
|  | char buffer[1024]; | 
|  | GLsizei length = 0; | 
|  | glGetShaderInfoLog(shader, sizeof(buffer), &length, buffer); | 
|  | std::string log(buffer, length); | 
|  | EXPECT_EQ(1, value) << "Error compiling shader: " << log; | 
|  | glDeleteShader(shader); | 
|  | shader = 0; | 
|  | } | 
|  | return shader; | 
|  | } | 
|  |  | 
|  | GLuint GLTestHelper::SetupProgram( | 
|  | GLuint vertex_shader, GLuint fragment_shader) { | 
|  | // Create the program object | 
|  | GLuint program = glCreateProgram(); | 
|  | glAttachShader(program, vertex_shader); | 
|  | glAttachShader(program, fragment_shader); | 
|  | // Link the program | 
|  | glLinkProgram(program); | 
|  | // Check the link status | 
|  | GLint linked = 0; | 
|  | glGetProgramiv(program, GL_LINK_STATUS, &linked); | 
|  | if (linked == 0) { | 
|  | char buffer[1024]; | 
|  | GLsizei length = 0; | 
|  | glGetProgramInfoLog(program, sizeof(buffer), &length, buffer); | 
|  | std::string log(buffer, length); | 
|  | EXPECT_EQ(1, linked) << "Error linking program: " << log; | 
|  | glDeleteProgram(program); | 
|  | program = 0; | 
|  | } | 
|  | return program; | 
|  | } | 
|  |  | 
|  | GLuint GLTestHelper::LoadProgram( | 
|  | const char* vertex_shader_source, | 
|  | const char* fragment_shader_source) { | 
|  | GLuint vertex_shader = LoadShader( | 
|  | GL_VERTEX_SHADER, vertex_shader_source); | 
|  | GLuint fragment_shader = LoadShader( | 
|  | GL_FRAGMENT_SHADER, fragment_shader_source); | 
|  | if (!vertex_shader || !fragment_shader) { | 
|  | return 0; | 
|  | } | 
|  | return SetupProgram(vertex_shader, fragment_shader); | 
|  | } | 
|  |  | 
|  | GLuint GLTestHelper::SetupUnitQuad(GLint position_location) { | 
|  | GLuint vbo = 0; | 
|  | glGenBuffers(1, &vbo); | 
|  | glBindBuffer(GL_ARRAY_BUFFER, vbo); | 
|  | static float vertices[] = { | 
|  | 1.0f,  1.0f, | 
|  | -1.0f,  1.0f, | 
|  | -1.0f, -1.0f, | 
|  | 1.0f,  1.0f, | 
|  | -1.0f, -1.0f, | 
|  | 1.0f, -1.0f, | 
|  | }; | 
|  | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | 
|  | glEnableVertexAttribArray(position_location); | 
|  | glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE, 0, 0); | 
|  |  | 
|  | return vbo; | 
|  | } | 
|  |  | 
|  | GLuint GLTestHelper::SetupColorsForUnitQuad( | 
|  | GLint location, const GLfloat color[4], GLenum usage) { | 
|  | GLuint vbo = 0; | 
|  | glGenBuffers(1, &vbo); | 
|  | glBindBuffer(GL_ARRAY_BUFFER, vbo); | 
|  | GLfloat vertices[6 * 4]; | 
|  | for (int ii = 0; ii < 6; ++ii) { | 
|  | for (int jj = 0; jj < 4; ++jj) { | 
|  | vertices[ii * 4 + jj] = color[jj]; | 
|  | } | 
|  | } | 
|  | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, usage); | 
|  | glEnableVertexAttribArray(location); | 
|  | glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, 0); | 
|  |  | 
|  | return vbo; | 
|  | } | 
|  |  | 
|  | bool GLTestHelper::CheckPixels( | 
|  | GLint x, GLint y, GLsizei width, GLsizei height, GLint tolerance, | 
|  | const uint8* color) { | 
|  | GLsizei size = width * height * 4; | 
|  | scoped_ptr<uint8[]> pixels(new uint8[size]); | 
|  | memset(pixels.get(), kCheckClearValue, size); | 
|  | glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get()); | 
|  | int bad_count = 0; | 
|  | for (GLint yy = 0; yy < height; ++yy) { | 
|  | for (GLint xx = 0; xx < width; ++xx) { | 
|  | int offset = yy * width * 4 + xx * 4; | 
|  | for (int jj = 0; jj < 4; ++jj) { | 
|  | uint8 actual = pixels[offset + jj]; | 
|  | uint8 expected = color[jj]; | 
|  | int diff = actual - expected; | 
|  | diff = diff < 0 ? -diff: diff; | 
|  | if (diff > tolerance) { | 
|  | EXPECT_EQ(expected, actual) << " at " << (xx + x) << ", " << (yy + y) | 
|  | << " channel " << jj; | 
|  | ++bad_count; | 
|  | // Exit early just so we don't spam the log but we print enough | 
|  | // to hopefully make it easy to diagnose the issue. | 
|  | if (bad_count > 16) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return bad_count == 0; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void Set16BitValue(uint8 dest[2], uint16 value) { | 
|  | dest[0] = value & 0xFFu; | 
|  | dest[1] = value >> 8; | 
|  | } | 
|  |  | 
|  | void Set32BitValue(uint8 dest[4], uint32 value) { | 
|  | dest[0] = (value >> 0) & 0xFFu; | 
|  | dest[1] = (value >> 8) & 0xFFu; | 
|  | dest[2] = (value >> 16) & 0xFFu; | 
|  | dest[3] = (value >> 24) & 0xFFu; | 
|  | } | 
|  |  | 
|  | struct BitmapHeaderFile { | 
|  | uint8 magic[2]; | 
|  | uint8 size[4]; | 
|  | uint8 reserved[4]; | 
|  | uint8 offset[4]; | 
|  | }; | 
|  |  | 
|  | struct BitmapInfoHeader{ | 
|  | uint8 size[4]; | 
|  | uint8 width[4]; | 
|  | uint8 height[4]; | 
|  | uint8 planes[2]; | 
|  | uint8 bit_count[2]; | 
|  | uint8 compression[4]; | 
|  | uint8 size_image[4]; | 
|  | uint8 x_pels_per_meter[4]; | 
|  | uint8 y_pels_per_meter[4]; | 
|  | uint8 clr_used[4]; | 
|  | uint8 clr_important[4]; | 
|  | }; | 
|  |  | 
|  | } | 
|  |  | 
|  | bool GLTestHelper::SaveBackbufferAsBMP( | 
|  | const char* filename, int width, int height) { | 
|  | FILE* fp = fopen(filename, "wb"); | 
|  | EXPECT_TRUE(fp != NULL); | 
|  | glPixelStorei(GL_PACK_ALIGNMENT, 1); | 
|  | int num_pixels = width * height; | 
|  | int size = num_pixels * 4; | 
|  | scoped_ptr<uint8[]> data(new uint8[size]); | 
|  | uint8* pixels = data.get(); | 
|  | glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); | 
|  |  | 
|  | // RGBA to BGRA | 
|  | for (int ii = 0; ii < num_pixels; ++ii) { | 
|  | int offset = ii * 4; | 
|  | uint8 t = pixels[offset + 0]; | 
|  | pixels[offset + 0] = pixels[offset + 2]; | 
|  | pixels[offset + 2] = t; | 
|  | } | 
|  |  | 
|  | BitmapHeaderFile bhf; | 
|  | BitmapInfoHeader bih; | 
|  |  | 
|  | bhf.magic[0] = 'B'; | 
|  | bhf.magic[1] = 'M'; | 
|  | Set32BitValue(bhf.size, 0); | 
|  | Set32BitValue(bhf.reserved, 0); | 
|  | Set32BitValue(bhf.offset, sizeof(bhf) + sizeof(bih)); | 
|  |  | 
|  | Set32BitValue(bih.size, sizeof(bih)); | 
|  | Set32BitValue(bih.width, width); | 
|  | Set32BitValue(bih.height, height); | 
|  | Set16BitValue(bih.planes, 1); | 
|  | Set16BitValue(bih.bit_count, 32); | 
|  | Set32BitValue(bih.compression, 0); | 
|  | Set32BitValue(bih.x_pels_per_meter, 0); | 
|  | Set32BitValue(bih.y_pels_per_meter, 0); | 
|  | Set32BitValue(bih.clr_used, 0); | 
|  | Set32BitValue(bih.clr_important, 0); | 
|  |  | 
|  | fwrite(&bhf, sizeof(bhf), 1, fp); | 
|  | fwrite(&bih, sizeof(bih), 1, fp); | 
|  | fwrite(pixels, size, 1, fp); | 
|  | fclose(fp); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | int GLTestHelper::RunTests(int argc, char** argv) { | 
|  | testing::InitGoogleMock(&argc, argv); | 
|  | return RUN_ALL_TESTS(); | 
|  | } |