// 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 #include #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(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(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 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 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(); }