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