1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <GLES2/gl2.h> 6#include <GLES2/gl2ext.h> 7#include <GLES2/gl2extchromium.h> 8 9#include "base/basictypes.h" 10#include "gpu/command_buffer/tests/gl_manager.h" 11#include "gpu/command_buffer/tests/gl_test_utils.h" 12#include "testing/gtest/include/gtest/gtest.h" 13 14#define SHADER(src) #src 15 16namespace gpu { 17 18static const uint16 kRedMask = 0xF800; 19static const uint16 kGreenMask = 0x07E0; 20static const uint16 kBlueMask = 0x001F; 21 22// Color palette in 565 format. 23static const uint16 kPalette[] = { 24 kGreenMask | kBlueMask, // Cyan. 25 kBlueMask | kRedMask, // Magenta. 26 kRedMask | kGreenMask, // Yellow. 27 0x0000, // Black. 28 kRedMask, // Red. 29 kGreenMask, // Green. 30 kBlueMask, // Blue. 31 0xFFFF, // White. 32}; 33static const unsigned kBlockSize = 4; 34static const unsigned kPaletteSize = sizeof(kPalette) / sizeof(kPalette[0]); 35static const unsigned kTextureWidth = kBlockSize * kPaletteSize; 36static const unsigned kTextureHeight = kBlockSize; 37 38static const char* extension(GLenum format) { 39 switch(format) { 40 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: 41 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: 42 return "GL_EXT_texture_compression_dxt1"; 43 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: 44 return "GL_CHROMIUM_texture_compression_dxt3"; 45 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: 46 return "GL_CHROMIUM_texture_compression_dxt5"; 47 default: 48 NOTREACHED(); 49 } 50 return NULL; 51} 52 53// Index that chooses the given colors (color_0 and color_1), 54// not the interpolated colors (color_2 and color_3). 55static const uint16 kColor0 = 0x0000; 56static const uint16 kColor1 = 0x5555; 57 58static GLuint LoadCompressedTexture(const void* data, 59 GLsizeiptr size, 60 GLenum format, 61 GLsizei width, 62 GLsizei height) { 63 GLuint texture; 64 glGenTextures(1, &texture); 65 glBindTexture(GL_TEXTURE_2D, texture); 66 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 67 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 68 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 69 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 70 glCompressedTexImage2D( 71 GL_TEXTURE_2D, 0, format, width, height, 0, size, data); 72 return texture; 73} 74 75GLuint LoadTextureDXT1(bool alpha) { 76 const unsigned kStride = 4; 77 uint16 data[kStride * kPaletteSize]; 78 for (unsigned i = 0; i < kPaletteSize; ++i) { 79 // Each iteration defines a 4x4 block of texture. 80 unsigned j = kStride * i; 81 data[j++] = kPalette[i]; // color_0. 82 data[j++] = kPalette[i]; // color_1. 83 data[j++] = kColor0; // color index. 84 data[j++] = kColor1; // color index. 85 } 86 GLenum format = alpha ? 87 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT; 88 return LoadCompressedTexture( 89 data, sizeof(data), format, kTextureWidth, kTextureHeight); 90} 91 92GLuint LoadTextureDXT3() { 93 const unsigned kStride = 8; 94 const uint16 kOpaque = 0xFFFF; 95 uint16 data[kStride * kPaletteSize]; 96 for (unsigned i = 0; i < kPaletteSize; ++i) { 97 // Each iteration defines a 4x4 block of texture. 98 unsigned j = kStride * i; 99 data[j++] = kOpaque; // alpha row 0. 100 data[j++] = kOpaque; // alpha row 1. 101 data[j++] = kOpaque; // alpha row 2. 102 data[j++] = kOpaque; // alpha row 3. 103 data[j++] = kPalette[i]; // color_0. 104 data[j++] = kPalette[i]; // color_1. 105 data[j++] = kColor0; // color index. 106 data[j++] = kColor1; // color index. 107 } 108 return LoadCompressedTexture(data, 109 sizeof(data), 110 GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 111 kTextureWidth, 112 kTextureHeight); 113} 114 115GLuint LoadTextureDXT5() { 116 const unsigned kStride = 8; 117 const uint16 kClear = 0x0000; 118 const uint16 kAlpha7 = 0xFFFF; // Opaque alpha index. 119 uint16 data[kStride * kPaletteSize]; 120 for (unsigned i = 0; i < kPaletteSize; ++i) { 121 // Each iteration defines a 4x4 block of texture. 122 unsigned j = kStride * i; 123 data[j++] = kClear; // alpha_0 | alpha_1. 124 data[j++] = kAlpha7; // alpha index. 125 data[j++] = kAlpha7; // alpha index. 126 data[j++] = kAlpha7; // alpha index. 127 data[j++] = kPalette[i]; // color_0. 128 data[j++] = kPalette[i]; // color_1. 129 data[j++] = kColor0; // color index. 130 data[j++] = kColor1; // color index. 131 } 132 return LoadCompressedTexture(data, 133 sizeof(data), 134 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 135 kTextureWidth, 136 kTextureHeight); 137} 138 139static void ToRGB888(uint16 rgb565, uint8 rgb888[]) { 140 uint8 r5 = (rgb565 & kRedMask) >> 11; 141 uint8 g6 = (rgb565 & kGreenMask) >> 5; 142 uint8 b5 = (rgb565 & kBlueMask); 143 // Replicate upper bits to lower empty bits. 144 rgb888[0] = (r5 << 3) | (r5 >> 2); 145 rgb888[1] = (g6 << 2) | (g6 >> 4); 146 rgb888[2] = (b5 << 3) | (b5 >> 2); 147} 148 149class CompressedTextureTest : public ::testing::TestWithParam<GLenum> { 150 protected: 151 virtual void SetUp() { 152 GLManager::Options options; 153 options.size = gfx::Size(kTextureWidth, kTextureHeight); 154 gl_.Initialize(options); 155 } 156 157 virtual void TearDown() { 158 gl_.Destroy(); 159 } 160 161 GLuint LoadProgram() { 162 const char* v_shader_src = SHADER( 163 attribute vec2 a_position; 164 varying vec2 v_texcoord; 165 void main() { 166 gl_Position = vec4(a_position, 0.0, 1.0); 167 v_texcoord = (a_position + 1.0) * 0.5; 168 } 169 ); 170 const char* f_shader_src = SHADER( 171 precision mediump float; 172 uniform sampler2D u_texture; 173 varying vec2 v_texcoord; 174 void main() { 175 gl_FragColor = texture2D(u_texture, v_texcoord); 176 } 177 ); 178 return GLTestHelper::LoadProgram(v_shader_src, f_shader_src); 179 } 180 181 GLuint LoadTexture(GLenum format) { 182 switch (format) { 183 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return LoadTextureDXT1(false); 184 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return LoadTextureDXT1(true); 185 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return LoadTextureDXT3(); 186 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return LoadTextureDXT5(); 187 default: NOTREACHED(); 188 } 189 return 0; 190 } 191 192 private: 193 GLManager gl_; 194}; 195 196// The test draws a texture in the given format and verifies that the drawn 197// pixels are of the same color as the texture. 198// The texture consists of 4x4 blocks of texels (same as DXT), one for each 199// color defined in kPalette. 200TEST_P(CompressedTextureTest, Draw) { 201 GLenum format = GetParam(); 202 203 // This test is only valid if compressed texture extension is supported. 204 const char* ext = extension(format); 205 if (!GLTestHelper::HasExtension(ext)) 206 return; 207 208 // Load shader program. 209 GLuint program = LoadProgram(); 210 ASSERT_NE(program, 0u); 211 GLint position_loc = glGetAttribLocation(program, "a_position"); 212 GLint texture_loc = glGetUniformLocation(program, "u_texture"); 213 ASSERT_NE(position_loc, -1); 214 ASSERT_NE(texture_loc, -1); 215 glUseProgram(program); 216 217 // Load geometry. 218 GLuint vbo = GLTestHelper::SetupUnitQuad(position_loc); 219 ASSERT_NE(vbo, 0u); 220 221 // Load texture. 222 GLuint texture = LoadTexture(format); 223 ASSERT_NE(texture, 0u); 224 glActiveTexture(GL_TEXTURE0); 225 glBindTexture(GL_TEXTURE_2D, texture); 226 glUniform1i(texture_loc, 0); 227 228 // Draw. 229 glDrawArrays(GL_TRIANGLES, 0, 6); 230 glFlush(); 231 232 // Verify results. 233 int origin[] = {0, 0}; 234 uint8 expected_rgba[] = {0, 0, 0, 255}; 235 for (unsigned i = 0; i < kPaletteSize; ++i) { 236 origin[0] = kBlockSize * i; 237 ToRGB888(kPalette[i], expected_rgba); 238 EXPECT_TRUE(GLTestHelper::CheckPixels(origin[0], origin[1], 239 kBlockSize, kBlockSize, 240 0, expected_rgba)); 241 } 242 GLTestHelper::CheckGLError("CompressedTextureTest.Draw", __LINE__); 243} 244 245static const GLenum kFormats[] = { 246 GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 247 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 248 GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 249 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 250}; 251INSTANTIATE_TEST_CASE_P(Format, 252 CompressedTextureTest, 253 ::testing::ValuesIn(kFormats)); 254 255} // namespace gpu 256