| // Copyright 2013 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 <GLES2/gl2.h> | |
| #include <GLES2/gl2ext.h> | |
| #include <GLES2/gl2extchromium.h> | |
| #include <stdint.h> | |
| #include "gpu/command_buffer/tests/gl_manager.h" | |
| #include "gpu/command_buffer/tests/gl_test_utils.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| #define SHADER(src) #src | |
| namespace gpu { | |
| static const uint16_t kRedMask = 0xF800; | |
| static const uint16_t kGreenMask = 0x07E0; | |
| static const uint16_t kBlueMask = 0x001F; | |
| // Color palette in 565 format. | |
| static const uint16_t kPalette[] = { | |
| kGreenMask | kBlueMask, // Cyan. | |
| kBlueMask | kRedMask, // Magenta. | |
| kRedMask | kGreenMask, // Yellow. | |
| 0x0000, // Black. | |
| kRedMask, // Red. | |
| kGreenMask, // Green. | |
| kBlueMask, // Blue. | |
| 0xFFFF, // White. | |
| }; | |
| static const unsigned kBlockSize = 4; | |
| static const unsigned kPaletteSize = sizeof(kPalette) / sizeof(kPalette[0]); | |
| static const unsigned kTextureWidth = kBlockSize * kPaletteSize; | |
| static const unsigned kTextureHeight = kBlockSize; | |
| static const char* extension(GLenum format) { | |
| switch(format) { | |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: | |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: | |
| return "GL_EXT_texture_compression_dxt1"; | |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: | |
| return "GL_CHROMIUM_texture_compression_dxt3"; | |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: | |
| return "GL_CHROMIUM_texture_compression_dxt5"; | |
| default: | |
| NOTREACHED(); | |
| } | |
| return NULL; | |
| } | |
| // Index that chooses the given colors (color_0 and color_1), | |
| // not the interpolated colors (color_2 and color_3). | |
| static const uint16_t kColor0 = 0x0000; | |
| static const uint16_t kColor1 = 0x5555; | |
| static GLuint LoadCompressedTexture(const void* data, | |
| GLsizeiptr size, | |
| GLenum format, | |
| GLsizei width, | |
| GLsizei height) { | |
| GLuint texture; | |
| glGenTextures(1, &texture); | |
| glBindTexture(GL_TEXTURE_2D, texture); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| glCompressedTexImage2D( | |
| GL_TEXTURE_2D, 0, format, width, height, 0, size, data); | |
| return texture; | |
| } | |
| GLuint LoadTextureDXT1(bool alpha) { | |
| const unsigned kStride = 4; | |
| uint16_t data[kStride * kPaletteSize]; | |
| for (unsigned i = 0; i < kPaletteSize; ++i) { | |
| // Each iteration defines a 4x4 block of texture. | |
| unsigned j = kStride * i; | |
| data[j++] = kPalette[i]; // color_0. | |
| data[j++] = kPalette[i]; // color_1. | |
| data[j++] = kColor0; // color index. | |
| data[j++] = kColor1; // color index. | |
| } | |
| GLenum format = alpha ? | |
| GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT; | |
| return LoadCompressedTexture( | |
| data, sizeof(data), format, kTextureWidth, kTextureHeight); | |
| } | |
| GLuint LoadTextureDXT3() { | |
| const unsigned kStride = 8; | |
| const uint16_t kOpaque = 0xFFFF; | |
| uint16_t data[kStride * kPaletteSize]; | |
| for (unsigned i = 0; i < kPaletteSize; ++i) { | |
| // Each iteration defines a 4x4 block of texture. | |
| unsigned j = kStride * i; | |
| data[j++] = kOpaque; // alpha row 0. | |
| data[j++] = kOpaque; // alpha row 1. | |
| data[j++] = kOpaque; // alpha row 2. | |
| data[j++] = kOpaque; // alpha row 3. | |
| data[j++] = kPalette[i]; // color_0. | |
| data[j++] = kPalette[i]; // color_1. | |
| data[j++] = kColor0; // color index. | |
| data[j++] = kColor1; // color index. | |
| } | |
| return LoadCompressedTexture(data, | |
| sizeof(data), | |
| GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, | |
| kTextureWidth, | |
| kTextureHeight); | |
| } | |
| GLuint LoadTextureDXT5() { | |
| const unsigned kStride = 8; | |
| const uint16_t kClear = 0x0000; | |
| const uint16_t kAlpha7 = 0xFFFF; // Opaque alpha index. | |
| uint16_t data[kStride * kPaletteSize]; | |
| for (unsigned i = 0; i < kPaletteSize; ++i) { | |
| // Each iteration defines a 4x4 block of texture. | |
| unsigned j = kStride * i; | |
| data[j++] = kClear; // alpha_0 | alpha_1. | |
| data[j++] = kAlpha7; // alpha index. | |
| data[j++] = kAlpha7; // alpha index. | |
| data[j++] = kAlpha7; // alpha index. | |
| data[j++] = kPalette[i]; // color_0. | |
| data[j++] = kPalette[i]; // color_1. | |
| data[j++] = kColor0; // color index. | |
| data[j++] = kColor1; // color index. | |
| } | |
| return LoadCompressedTexture(data, | |
| sizeof(data), | |
| GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, | |
| kTextureWidth, | |
| kTextureHeight); | |
| } | |
| static void ToRGB888(uint16_t rgb565, uint8_t rgb888[]) { | |
| uint8_t r5 = (rgb565 & kRedMask) >> 11; | |
| uint8_t g6 = (rgb565 & kGreenMask) >> 5; | |
| uint8_t b5 = (rgb565 & kBlueMask); | |
| // Replicate upper bits to lower empty bits. | |
| rgb888[0] = (r5 << 3) | (r5 >> 2); | |
| rgb888[1] = (g6 << 2) | (g6 >> 4); | |
| rgb888[2] = (b5 << 3) | (b5 >> 2); | |
| } | |
| class CompressedTextureTest : public ::testing::TestWithParam<GLenum> { | |
| protected: | |
| void SetUp() override { | |
| GLManager::Options options; | |
| options.size = gfx::Size(kTextureWidth, kTextureHeight); | |
| gl_.Initialize(options); | |
| } | |
| void TearDown() override { gl_.Destroy(); } | |
| GLuint LoadProgram() { | |
| const char* v_shader_src = SHADER( | |
| attribute vec2 a_position; | |
| varying vec2 v_texcoord; | |
| void main() { | |
| gl_Position = vec4(a_position, 0.0, 1.0); | |
| v_texcoord = (a_position + 1.0) * 0.5; | |
| } | |
| ); | |
| const char* f_shader_src = SHADER( | |
| precision mediump float; | |
| uniform sampler2D u_texture; | |
| varying vec2 v_texcoord; | |
| void main() { | |
| gl_FragColor = texture2D(u_texture, v_texcoord); | |
| } | |
| ); | |
| return GLTestHelper::LoadProgram(v_shader_src, f_shader_src); | |
| } | |
| GLuint LoadTexture(GLenum format) { | |
| switch (format) { | |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return LoadTextureDXT1(false); | |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return LoadTextureDXT1(true); | |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return LoadTextureDXT3(); | |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return LoadTextureDXT5(); | |
| default: NOTREACHED(); | |
| } | |
| return 0; | |
| } | |
| private: | |
| GLManager gl_; | |
| }; | |
| // The test draws a texture in the given format and verifies that the drawn | |
| // pixels are of the same color as the texture. | |
| // The texture consists of 4x4 blocks of texels (same as DXT), one for each | |
| // color defined in kPalette. | |
| TEST_P(CompressedTextureTest, Draw) { | |
| GLenum format = GetParam(); | |
| // This test is only valid if compressed texture extension is supported. | |
| const char* ext = extension(format); | |
| if (!GLTestHelper::HasExtension(ext)) | |
| return; | |
| // Load shader program. | |
| GLuint program = LoadProgram(); | |
| ASSERT_NE(program, 0u); | |
| GLint position_loc = glGetAttribLocation(program, "a_position"); | |
| GLint texture_loc = glGetUniformLocation(program, "u_texture"); | |
| ASSERT_NE(position_loc, -1); | |
| ASSERT_NE(texture_loc, -1); | |
| glUseProgram(program); | |
| // Load geometry. | |
| GLuint vbo = GLTestHelper::SetupUnitQuad(position_loc); | |
| ASSERT_NE(vbo, 0u); | |
| // Load texture. | |
| GLuint texture = LoadTexture(format); | |
| ASSERT_NE(texture, 0u); | |
| glActiveTexture(GL_TEXTURE0); | |
| glBindTexture(GL_TEXTURE_2D, texture); | |
| glUniform1i(texture_loc, 0); | |
| // Draw. | |
| glDrawArrays(GL_TRIANGLES, 0, 6); | |
| glFlush(); | |
| // Verify results. | |
| int origin[] = {0, 0}; | |
| uint8_t expected_rgba[] = {0, 0, 0, 255}; | |
| for (unsigned i = 0; i < kPaletteSize; ++i) { | |
| origin[0] = kBlockSize * i; | |
| ToRGB888(kPalette[i], expected_rgba); | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(origin[0], origin[1], kBlockSize, | |
| kBlockSize, 0, expected_rgba, | |
| nullptr)); | |
| } | |
| GLTestHelper::CheckGLError("CompressedTextureTest.Draw", __LINE__); | |
| } | |
| static const GLenum kFormats[] = { | |
| GL_COMPRESSED_RGB_S3TC_DXT1_EXT, | |
| GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, | |
| GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, | |
| GL_COMPRESSED_RGBA_S3TC_DXT5_EXT | |
| }; | |
| INSTANTIATE_TEST_CASE_P(Format, | |
| CompressedTextureTest, | |
| ::testing::ValuesIn(kFormats)); | |
| } // namespace gpu |