1// Copyright (c) 2010 The Chromium OS 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 <stdio.h>
6#include <sys/mman.h>
7
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10
11#include "main.h"
12#include "testbase.h"
13#include "utils.h"
14#include "yuv2rgb.h"
15
16namespace glbench {
17
18class YuvToRgbTest : public DrawArraysTestFunc {
19 public:
20  YuvToRgbTest() {
21    memset(textures_, 0, sizeof(textures_));
22  }
23  virtual ~YuvToRgbTest() {
24    glDeleteTextures(arraysize(textures_), textures_);
25  }
26  virtual bool Run();
27  virtual const char* Name() const { return "yuv_to_rgb"; }
28
29  enum YuvTestFlavor {
30    YUV_PLANAR_ONE_TEXTURE_SLOW,
31    YUV_PLANAR_ONE_TEXTURE_FASTER,
32    YUV_PLANAR_THREE_TEXTURES,
33    YUV_SEMIPLANAR_TWO_TEXTURES,
34  };
35
36 private:
37  GLuint textures_[6];
38  YuvTestFlavor flavor_;
39  GLuint YuvToRgbShaderProgram(GLuint vertex_buffer, int width, int height);
40  bool SetupTextures();
41  DISALLOW_COPY_AND_ASSIGN(YuvToRgbTest);
42};
43
44
45GLuint YuvToRgbTest::YuvToRgbShaderProgram(GLuint vertex_buffer,
46                                           int width, int height) {
47  const char *vertex = NULL;
48  const char *fragment = NULL;
49
50  switch (flavor_) {
51    case YUV_PLANAR_ONE_TEXTURE_SLOW:
52      vertex = YUV2RGB_VERTEX_1;
53      fragment = YUV2RGB_FRAGMENT_1;
54      break;
55    case YUV_PLANAR_ONE_TEXTURE_FASTER:
56      vertex = YUV2RGB_VERTEX_2;
57      fragment = YUV2RGB_FRAGMENT_2;
58      break;
59    case YUV_PLANAR_THREE_TEXTURES:
60      vertex = YUV2RGB_VERTEX_34;
61      fragment = YUV2RGB_FRAGMENT_3;
62      break;
63    case YUV_SEMIPLANAR_TWO_TEXTURES:
64      vertex = YUV2RGB_VERTEX_34;
65      fragment = YUV2RGB_FRAGMENT_4;
66      break;
67  }
68
69  size_t size_vertex = 0;
70  size_t size_fragment = 0;
71  char *yuv_to_rgb_vertex = static_cast<char *>(
72      MmapFile(vertex, &size_vertex));
73  char *yuv_to_rgb_fragment = static_cast<char *>(
74      MmapFile(fragment, &size_fragment));
75  GLuint program = 0;
76
77  if (!yuv_to_rgb_fragment || !yuv_to_rgb_vertex)
78    goto done;
79
80  {
81    program = InitShaderProgramWithHeader(NULL, yuv_to_rgb_vertex,
82                                          yuv_to_rgb_fragment);
83
84    int imageWidthUniform = glGetUniformLocation(program, "imageWidth");
85    int imageHeightUniform = glGetUniformLocation(program, "imageHeight");
86
87    int textureSampler = glGetUniformLocation(program, "textureSampler");
88    int evenLinesSampler = glGetUniformLocation(program, "paritySampler");
89    int ySampler = glGetUniformLocation(program, "ySampler");
90    int uSampler = glGetUniformLocation(program, "uSampler");
91    int vSampler = glGetUniformLocation(program, "vSampler");
92    int uvSampler = glGetUniformLocation(program, "uvSampler");
93
94    glUniform1f(imageWidthUniform, width);
95    glUniform1f(imageHeightUniform, height);
96    glUniform1i(textureSampler, 0);
97    glUniform1i(evenLinesSampler, 1);
98
99    glUniform1i(ySampler, 2);
100    glUniform1i(uSampler, 3);
101    glUniform1i(vSampler, 4);
102    glUniform1i(uvSampler, 5);
103
104    {
105      // This is used only if USE_UNIFORM_MATRIX is enabled in fragment
106      // shaders.
107      float c[] = {
108        1.0,    1.0,    1.0,   0.0,
109        0.0,   -0.344,  1.772, 0.0,
110        1.402, -0.714,  0.0,   0.0,
111        -0.701,  0.529, -0.886, 1.0
112      };
113      int conversion = glGetUniformLocation(program, "conversion");
114      glUniformMatrix4fv(conversion, 1, GL_FALSE, c);
115      assert(glGetError() == 0);
116    }
117
118    int attribute_index = glGetAttribLocation(program, "c");
119    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
120    glVertexAttribPointer(attribute_index, 2, GL_FLOAT, GL_FALSE, 0, NULL);
121    glEnableVertexAttribArray(attribute_index);
122    return program;
123  }
124
125
126done:
127  munmap(yuv_to_rgb_fragment, size_fragment);
128  munmap(yuv_to_rgb_fragment, size_vertex);
129  return program;
130}
131
132
133bool YuvToRgbTest::SetupTextures() {
134  bool ret = false;
135  size_t size = 0;
136  char evenodd[2] = {0, static_cast<char>(-1)};
137  char* pixels = static_cast<char *>(MmapFile(YUV2RGB_NAME, &size));
138  const int luma_size = YUV2RGB_WIDTH * YUV2RGB_PIXEL_HEIGHT;
139  const int chroma_size = YUV2RGB_WIDTH/2 * YUV2RGB_PIXEL_HEIGHT/2;
140  const char* u_plane = pixels + luma_size;
141  const char* v_plane = pixels + luma_size + chroma_size;
142  if (!pixels) {
143    printf("# Error: Could not open image file: %s\n", YUV2RGB_NAME);
144    goto done;
145  }
146  if (size != YUV2RGB_SIZE) {
147    printf("# Error: Image file of wrong size, got %d, expected %d\n",
148           static_cast<int>(size), YUV2RGB_SIZE);
149    goto done;
150  }
151
152  glGenTextures(arraysize(textures_), textures_);
153  glActiveTexture(GL_TEXTURE0);
154  glBindTexture(GL_TEXTURE_2D, textures_[0]);
155  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, YUV2RGB_WIDTH, YUV2RGB_HEIGHT,
156               0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
157
158  glActiveTexture(GL_TEXTURE1);
159  glBindTexture(GL_TEXTURE_2D, textures_[1]);
160  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 2, 1,
161               0, GL_LUMINANCE, GL_UNSIGNED_BYTE, evenodd);
162
163  glActiveTexture(GL_TEXTURE2);
164  glBindTexture(GL_TEXTURE_2D, textures_[2]);
165  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
166               YUV2RGB_WIDTH, YUV2RGB_PIXEL_HEIGHT,
167               0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
168
169  glActiveTexture(GL_TEXTURE3);
170  glBindTexture(GL_TEXTURE_2D, textures_[3]);
171  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
172               YUV2RGB_WIDTH/2, YUV2RGB_PIXEL_HEIGHT/2,
173               0, GL_LUMINANCE, GL_UNSIGNED_BYTE, u_plane);
174
175  glActiveTexture(GL_TEXTURE4);
176  glBindTexture(GL_TEXTURE_2D, textures_[4]);
177  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
178               YUV2RGB_WIDTH/2, YUV2RGB_PIXEL_HEIGHT/2,
179               0, GL_LUMINANCE, GL_UNSIGNED_BYTE, v_plane);
180
181  {
182    scoped_ptr<char[]> buf_uv(new char[chroma_size * 2]);
183    char* buf_uv_ptr = buf_uv.get();
184    for (int i = 0; i < chroma_size; i++) {
185        *buf_uv_ptr++ = u_plane[i];
186        *buf_uv_ptr++ = v_plane[i];
187    }
188
189    glActiveTexture(GL_TEXTURE5);
190    glBindTexture(GL_TEXTURE_2D, textures_[5]);
191    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
192                 YUV2RGB_WIDTH/2, YUV2RGB_PIXEL_HEIGHT/2,
193                 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, buf_uv.get());
194  }
195
196  for (unsigned int i = 0; i < arraysize(textures_); i++) {
197    glActiveTexture(GL_TEXTURE0 + i);
198    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
199    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
200    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
201    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
202  }
203
204  ret = true;
205
206done:
207  munmap(pixels, size);
208  return ret;
209}
210
211
212bool YuvToRgbTest::Run() {
213  glClearColor(0.f, 1.f, 0.f, 1.f);
214
215  GLuint program = 0;
216  GLuint vertex_buffer = 0;
217  GLfloat vertices[8] = {
218    0.f, 0.f,
219    1.f, 0.f,
220    0.f, 1.f,
221    1.f, 1.f,
222  };
223  vertex_buffer = SetupVBO(GL_ARRAY_BUFFER, sizeof(vertices), vertices);
224
225  if (!SetupTextures())
226    return false;
227
228  glViewport(0, 0, YUV2RGB_WIDTH, YUV2RGB_PIXEL_HEIGHT);
229
230  YuvTestFlavor flavors[] = {
231    YUV_PLANAR_ONE_TEXTURE_SLOW, YUV_PLANAR_ONE_TEXTURE_FASTER,
232    YUV_PLANAR_THREE_TEXTURES, YUV_SEMIPLANAR_TWO_TEXTURES
233  };
234  const char* flavor_names[] = {
235    "yuv_shader_1", "yuv_shader_2", "yuv_shader_3", "yuv_shader_4"
236  };
237  for (unsigned int f = 0; f < arraysize(flavors); f++) {
238    flavor_ = flavors[f];
239
240    program = YuvToRgbShaderProgram(vertex_buffer,
241                                    YUV2RGB_WIDTH, YUV2RGB_PIXEL_HEIGHT);
242    if (program) {
243      FillRateTestNormalSubWindow(flavor_names[f],
244                                  std::min(YUV2RGB_WIDTH, g_width),
245                                  std::min(YUV2RGB_PIXEL_HEIGHT, g_height));
246    } else {
247      printf("# Error: Could not set up YUV shader.\n");
248    }
249
250    glDeleteProgram(program);
251  }
252
253  glDeleteBuffers(1, &vertex_buffer);
254
255  return true;
256}
257
258
259TestBase* GetYuvToRgbTest() {
260  return new YuvToRgbTest();
261}
262
263} // namespace glbench
264