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 
16 namespace glbench {
17 
18 class YuvToRgbTest : public DrawArraysTestFunc {
19  public:
YuvToRgbTest()20   YuvToRgbTest() {
21     memset(textures_, 0, sizeof(textures_));
22   }
~YuvToRgbTest()23   virtual ~YuvToRgbTest() {
24     glDeleteTextures(arraysize(textures_), textures_);
25   }
26   virtual bool Run();
Name() const27   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 
YuvToRgbShaderProgram(GLuint vertex_buffer,int width,int height)45 GLuint 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 
126 done:
127   munmap(yuv_to_rgb_fragment, size_fragment);
128   munmap(yuv_to_rgb_fragment, size_vertex);
129   return program;
130 }
131 
132 
SetupTextures()133 bool 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 
206 done:
207   munmap(pixels, size);
208   return ret;
209 }
210 
211 
Run()212 bool 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 
GetYuvToRgbTest()259 TestBase* GetYuvToRgbTest() {
260   return new YuvToRgbTest();
261 }
262 
263 } // namespace glbench
264