| // 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_ANGLE_texture_compression_dxt1"; |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
| return "GL_ANGLE_texture_compression_dxt3"; |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
| return "GL_ANGLE_texture_compression_dxt5"; |
| default: |
| NOTREACHED(); |
| } |
| return nullptr; |
| } |
| |
| // 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_SUITE_P(Format, |
| CompressedTextureTest, |
| ::testing::ValuesIn(kFormats)); |
| |
| } // namespace gpu |