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