• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <EGL/egl.h>
18 #include <EGL/eglext.h>
19 #include <GLES2/gl2.h>
20 #include <GLES2/gl2ext.h>
21 #include <jni.h>
22 #include <stdlib.h>
23 #include <android/hardware_buffer.h>
24 #include <android/log.h>
25 #include <cmath>
26 #include <string>
27 #include <sstream>
28 
29 #define  LOG_TAG    "VrExtensionsJni"
30 #define  LOGV(...)  __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
31 
32 using PFNEGLGETNATIVECLIENTBUFFERANDROID =
33         EGLClientBuffer(EGLAPIENTRYP)(const AHardwareBuffer* buffer);
34 
35 using PFNGLEGLIMAGETARGETTEXTURE2DOESPROC = void(GL_APIENTRYP)(GLenum target,
36                                                                void* image);
37 
38 using PFNGLBUFFERSTORAGEEXTERNALEXTPROC =
39     void(GL_APIENTRYP)(GLenum target, GLintptr offset, GLsizeiptr size,
40                        void* clientBuffer, GLbitfield flags);
41 
42 using PFNGLMAPBUFFERRANGEPROC = void*(GL_APIENTRYP)(GLenum target,
43                                                     GLintptr offset,
44                                                     GLsizeiptr length,
45                                                     GLbitfield access);
46 
47 using PFNGLUNMAPBUFFERPROC = void*(GL_APIENTRYP)(GLenum target);
48 
49 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
50 PFNEGLGETNATIVECLIENTBUFFERANDROID eglGetNativeClientBufferANDROID;
51 PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
52 PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
53 PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC
54     glFramebufferTextureMultisampleMultiviewOVR;
55 PFNGLBUFFERSTORAGEEXTERNALEXTPROC glBufferStorageExternalEXT;
56 PFNGLMAPBUFFERRANGEPROC glMapBufferRange;
57 PFNGLUNMAPBUFFERPROC glUnmapBuffer;
58 
59 #define NO_ERROR 0
60 #define GL_UNIFORM_BUFFER         0x8A11
61 
62 // Declare flags that are added to MapBufferRange via EXT_buffer_storage.
63 // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_buffer_storage.txt
64 #define GL_MAP_PERSISTENT_BIT_EXT 0x0040
65 #define GL_MAP_COHERENT_BIT_EXT   0x0080
66 
67 // Declare tokens added as a part of EGL_EXT_image_gl_colorspace.
68 #define EGL_GL_COLORSPACE_DEFAULT_EXT 0x314D
69 
70 #define LOAD_PROC(NAME, TYPE)                                           \
71     NAME = reinterpret_cast<TYPE>(eglGetProcAddress(# NAME))
72 
73 #define ASSERT(condition, format, args...)      \
74     if (!(condition)) {                         \
75         fail(env, format, ## args);             \
76         return;                                 \
77     }
78 
79 #define ASSERT_TRUE(a) \
80     ASSERT((a), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
81 #define ASSERT_FALSE(a) \
82     ASSERT(!(a), "assert failed on (!" #a ") at " __FILE__ ":%d", __LINE__)
83 #define ASSERT_EQ(a, b) \
84     ASSERT((a) == (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
85 #define ASSERT_NE(a, b) \
86     ASSERT((a) != (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
87 #define ASSERT_GT(a, b) \
88     ASSERT((a) > (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
89 #define ASSERT_NEAR(a, b, delta)                     \
90     ASSERT((a - delta) <= (b) && (b) <= (a + delta), \
91            "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
92 
fail(JNIEnv * env,const char * format,...)93 void fail(JNIEnv* env, const char* format, ...) {
94     va_list args;
95     va_start(args, format);
96     char* msg;
97     vasprintf(&msg, format, args);
98     va_end(args);
99     jclass exClass;
100     const char* className = "java/lang/AssertionError";
101     exClass = env->FindClass(className);
102     env->ThrowNew(exClass, msg);
103     free(msg);
104 }
105 
testEglImageArray(JNIEnv * env,AHardwareBuffer_Desc desc,int nsamples)106 static void testEglImageArray(JNIEnv* env, AHardwareBuffer_Desc desc,
107                               int nsamples) {
108     ASSERT_GT(desc.layers, 1);
109     AHardwareBuffer* hwbuffer = nullptr;
110     int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
111     ASSERT_FALSE(error);
112     // Create EGLClientBuffer from the AHardwareBuffer.
113     EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
114     ASSERT_TRUE(native_buffer);
115     // Create EGLImage from EGLClientBuffer.
116     EGLint attrs[] = {EGL_NONE};
117     EGLImageKHR image =
118         eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
119                           EGL_NATIVE_BUFFER_ANDROID, native_buffer, attrs);
120     ASSERT_TRUE(image);
121     // Create OpenGL texture from the EGLImage.
122     GLuint texid;
123     glGenTextures(1, &texid);
124     glBindTexture(GL_TEXTURE_2D_ARRAY, texid);
125     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D_ARRAY, image);
126     ASSERT_EQ(glGetError(), GL_NO_ERROR);
127     // Create FBO and add multiview attachment.
128     GLuint fboid;
129     glGenFramebuffers(1, &fboid);
130     glBindFramebuffer(GL_FRAMEBUFFER, fboid);
131     const GLint miplevel = 0;
132     const GLint base_view = 0;
133     const GLint num_views = desc.layers;
134     if (nsamples == 1) {
135         glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
136                                          texid, miplevel, base_view, num_views);
137     } else {
138         glFramebufferTextureMultisampleMultiviewOVR(
139             GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texid, miplevel, nsamples,
140             base_view, num_views);
141     }
142     ASSERT_EQ(glGetError(), GL_NO_ERROR);
143     ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
144               GL_FRAMEBUFFER_COMPLETE);
145     // Release memory.
146     glDeleteTextures(1, &texid);
147     glDeleteFramebuffers(1, &fboid);
148     AHardwareBuffer_release(hwbuffer);
149 }
150 
151 extern "C" JNIEXPORT void JNICALL
Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestEglImageArray(JNIEnv * env,jclass)152 Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestEglImageArray(
153     JNIEnv* env, jclass /* unused */) {
154     // First, load entry points provided by extensions.
155     LOAD_PROC(glEGLImageTargetTexture2DOES,
156               PFNGLEGLIMAGETARGETTEXTURE2DOESPROC);
157     ASSERT_NE(glEGLImageTargetTexture2DOES, nullptr);
158     LOAD_PROC(eglGetNativeClientBufferANDROID,
159               PFNEGLGETNATIVECLIENTBUFFERANDROID);
160     ASSERT_NE(eglGetNativeClientBufferANDROID, nullptr);
161     LOAD_PROC(eglCreateImageKHR, PFNEGLCREATEIMAGEKHRPROC);
162     ASSERT_NE(eglCreateImageKHR, nullptr);
163     LOAD_PROC(glFramebufferTextureMultiviewOVR,
164               PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC);
165     ASSERT_NE(glFramebufferTextureMultiviewOVR, nullptr);
166     LOAD_PROC(glFramebufferTextureMultisampleMultiviewOVR,
167               PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC);
168     ASSERT_NE(glFramebufferTextureMultisampleMultiviewOVR, nullptr);
169     // Try creating a 32x32 AHardwareBuffer and attaching it to a multiview
170     // framebuffer, with various formats and depths.
171     AHardwareBuffer_Desc desc = {};
172     desc.width = 32;
173     desc.height = 32;
174     desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
175                  AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
176     const int layers[] = {2, 4};
177     const int formats[] = {
178       AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
179       AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
180       AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
181       AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
182       // Do not test AHARDWAREBUFFER_FORMAT_BLOB, it isn't color-renderable.
183     };
184     const int samples[] = {1, 2, 4};
185     for (int nsamples : samples) {
186       for (auto nlayers : layers) {
187         for (auto format : formats) {
188           desc.layers = nlayers;
189           desc.format = format;
190           testEglImageArray(env, desc, nsamples);
191         }
192       }
193     }
194 }
195 
testExternalBuffer(JNIEnv * env,uint64_t usage,bool write_hwbuffer,const std::string & test_string)196 static void testExternalBuffer(JNIEnv* env, uint64_t usage, bool write_hwbuffer,
197                                const std::string& test_string) {
198     // Create a blob AHardwareBuffer suitable for holding the string.
199     AHardwareBuffer_Desc desc = {};
200     desc.width = test_string.size();
201     desc.height = 1;
202     desc.layers = 1;
203     desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
204     desc.usage = usage;
205     AHardwareBuffer* hwbuffer = nullptr;
206     int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
207     ASSERT_EQ(error, NO_ERROR);
208     // Create EGLClientBuffer from the AHardwareBuffer.
209     EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
210     ASSERT_TRUE(native_buffer);
211     // Create uniform buffer from EGLClientBuffer.
212     const GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
213         GL_MAP_COHERENT_BIT_EXT | GL_MAP_PERSISTENT_BIT_EXT;
214     GLuint buf = 0;
215     glGenBuffers(1, &buf);
216     glBindBuffer(GL_UNIFORM_BUFFER, buf);
217     ASSERT_EQ(glGetError(), GL_NO_ERROR);
218     const GLsizeiptr bufsize = desc.width * desc.height;
219     glBufferStorageExternalEXT(GL_UNIFORM_BUFFER, 0,
220              bufsize, native_buffer, flags);
221     ASSERT_EQ(glGetError(), GL_NO_ERROR);
222     // Obtain a writeable pointer using either OpenGL or the Android API,
223     // then copy the test string into it.
224     if (write_hwbuffer) {
225       void* data = nullptr;
226       error = AHardwareBuffer_lock(hwbuffer,
227                                    AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, -1,
228                                    NULL, &data);
229       ASSERT_EQ(error, NO_ERROR);
230       ASSERT_TRUE(data);
231       memcpy(data, test_string.c_str(), test_string.size());
232       error = AHardwareBuffer_unlock(hwbuffer, nullptr);
233       ASSERT_EQ(error, NO_ERROR);
234     } else {
235       void* data =
236           glMapBufferRange(GL_UNIFORM_BUFFER, 0, bufsize,
237                            GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT_EXT);
238       ASSERT_EQ(glGetError(), GL_NO_ERROR);
239       ASSERT_TRUE(data);
240       memcpy(data, test_string.c_str(), test_string.size());
241       glUnmapBuffer(GL_UNIFORM_BUFFER);
242       ASSERT_EQ(glGetError(), GL_NO_ERROR);
243     }
244     // Obtain a readable pointer and verify the data.
245     void* data = glMapBufferRange(GL_UNIFORM_BUFFER, 0, bufsize, GL_MAP_READ_BIT);
246     ASSERT_TRUE(data);
247     ASSERT_EQ(strncmp(static_cast<char*>(data), test_string.c_str(),
248                       test_string.size()), 0);
249     glUnmapBuffer(GL_UNIFORM_BUFFER);
250     ASSERT_EQ(glGetError(), GL_NO_ERROR);
251     AHardwareBuffer_release(hwbuffer);
252 }
253 
254 extern "C" JNIEXPORT void JNICALL
Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestExternalBuffer(JNIEnv * env,jclass)255 Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestExternalBuffer(
256     JNIEnv* env, jclass /* unused */) {
257     // First, check for EXT_external_buffer in the extension string.
258     auto exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
259     ASSERT_TRUE(exts && strstr(exts, "GL_EXT_external_buffer"));
260     // Next, load entry points provided by extensions.
261     LOAD_PROC(eglGetNativeClientBufferANDROID, PFNEGLGETNATIVECLIENTBUFFERANDROID);
262     ASSERT_NE(eglGetNativeClientBufferANDROID, nullptr);
263     LOAD_PROC(glBufferStorageExternalEXT, PFNGLBUFFERSTORAGEEXTERNALEXTPROC);
264     ASSERT_NE(glBufferStorageExternalEXT, nullptr);
265     LOAD_PROC(glMapBufferRange, PFNGLMAPBUFFERRANGEPROC);
266     ASSERT_NE(glMapBufferRange, nullptr);
267     LOAD_PROC(glUnmapBuffer, PFNGLUNMAPBUFFERPROC);
268     ASSERT_NE(glUnmapBuffer, nullptr);
269     const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
270         AHARDWAREBUFFER_USAGE_CPU_READ_RARELY |
271         AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER |
272         AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA;
273     const std::string test_string = "Hello, world.";
274     // First try writing to the buffer using OpenGL, then try writing to it via
275     // the AHardwareBuffer API.
276     testExternalBuffer(env, usage, false, test_string);
277     testExternalBuffer(env, usage, true, test_string);
278 }
279 
280 const GLchar* const kSrgbVertexCode = R"(
281     // vertex position in clip space (-1..1)
282     attribute vec4 position;
283     varying mediump vec2 uv;
284     void main() {
285       gl_Position = position;
286       uv = vec2(0.5 * (position.x + 1.0), 0.5);
287     })";
288 
289 const GLchar* const kSrgbFragmentCode = R"(
290     varying mediump vec2 uv;
291     uniform sampler2D tex;
292     void main() {
293       gl_FragColor = texture2D(tex, uv);
294     })";
295 
SrgbChannelToLinear(float cs)296 static inline float SrgbChannelToLinear(float cs) {
297     if (cs <= 0.04045)
298         return cs / 12.92f;
299     else
300         return std::pow((cs + 0.055f) / 1.055f, 2.4f);
301 }
302 
LinearChannelToSrgb(float cs)303 static inline float LinearChannelToSrgb(float cs) {
304     if (cs <= 0.0f)
305         return 0.0f;
306     else if (cs < 0.0031308f)
307         return 12.92f * cs;
308     else if (cs < 1.0f)
309         return 1.055f * std::pow(cs, 0.41666f) - 0.055f;
310     else
311         return 1.0f;
312 }
313 
SrgbColorToLinear(uint32_t color)314 static uint32_t SrgbColorToLinear(uint32_t color) {
315     float r = SrgbChannelToLinear((color & 0xff) / 255.0f);
316     float g = SrgbChannelToLinear(((color >> 8) & 0xff) / 255.0f);
317     float b = SrgbChannelToLinear(((color >> 16) & 0xff) / 255.0f);
318     uint32_t r8 = r * 255.0f;
319     uint32_t g8 = g * 255.0f;
320     uint32_t b8 = b * 255.0f;
321     uint32_t a8 = color >> 24;
322     return (a8 << 24) | (b8 << 16) | (g8 << 8) | r8;
323 }
324 
LinearColorToSrgb(uint32_t color)325 static uint32_t LinearColorToSrgb(uint32_t color) {
326     float r = LinearChannelToSrgb((color & 0xff) / 255.0f);
327     float g = LinearChannelToSrgb(((color >> 8) & 0xff) / 255.0f);
328     float b = LinearChannelToSrgb(((color >> 16) & 0xff) / 255.0f);
329     uint32_t r8 = r * 255.0f;
330     uint32_t g8 = g * 255.0f;
331     uint32_t b8 = b * 255.0f;
332     uint32_t a8 = color >> 24;
333     return (a8 << 24) | (b8 << 16) | (g8 << 8) | r8;
334 }
335 
LerpColor(uint32_t color0,uint32_t color1,float t)336 static uint32_t LerpColor(uint32_t color0, uint32_t color1, float t) {
337     float r0 = (color0 & 0xff) / 255.0f;
338     float g0 = ((color0 >> 8) & 0xff) / 255.0f;
339     float b0 = ((color0 >> 16) & 0xff) / 255.0f;
340     float a0 = ((color0 >> 24) & 0xff) / 255.0f;
341     float r1 = (color1 & 0xff) / 255.0f;
342     float g1 = ((color1 >> 8) & 0xff) / 255.0f;
343     float b1 = ((color1 >> 16) & 0xff) / 255.0f;
344     float a1 = ((color1 >> 24) & 0xff) / 255.0f;
345     uint32_t r8 = (r0 * (1.0f - t) + r1 * t) * 255.0f;
346     uint32_t g8 = (g0 * (1.0f - t) + g1 * t) * 255.0f;
347     uint32_t b8 = (b0 * (1.0f - t) + b1 * t) * 255.0f;
348     uint32_t a8 = (a0 * (1.0f - t) + a1 * t) * 255.0f;
349     return (a8 << 24) | (b8 << 16) | (g8 << 8) | r8;
350 }
351 
352 // Choose an odd-numbered framebuffer width so that we can
353 // extract the middle pixel of a gradient.
354 constexpr uint32_t kFramebufferWidth = 31;
355 
356 // Declare the pixel data for the 2x1 texture.
357 // Color components are ordered like this: AABBGGRR
358 constexpr uint32_t kTextureData[] = {
359     0xff800000,  // Half-Blue
360     0xff000080,  // Half-Red
361 };
362 constexpr uint32_t kTextureWidth = sizeof(kTextureData) / sizeof(kTextureData[0]);
363 
364 // Declare expected values for the middle pixel for various sampling behaviors.
365 const uint32_t kExpectedMiddlePixel_NoSrgb = LerpColor(kTextureData[0], kTextureData[1], 0.5f);
366 const uint32_t kExpectedMiddlePixel_LinearizeAfterFiltering =
367     SrgbColorToLinear(kExpectedMiddlePixel_NoSrgb);
368 const uint32_t kExpectedMiddlePixel_LinearizeBeforeFiltering =
369     LerpColor(SrgbColorToLinear(kTextureData[0]), SrgbColorToLinear(kTextureData[1]), 0.5f);
370 
371 // Declare expected values for the final pixel color for various blending behaviors.
372 constexpr uint32_t kBlendDestColor = 0xff000080;
373 constexpr uint32_t kBlendSourceColor = 0x80800000;
374 const uint32_t kExpectedBlendedPixel_NoSrgb = LerpColor(kBlendSourceColor, kBlendDestColor, 0.5f);
375 const uint32_t kExpectedBlendedPixel_Srgb =
376     LinearColorToSrgb(LerpColor(kBlendSourceColor, SrgbColorToLinear(kBlendDestColor), 0.5f));
377 
378 // Define a set of test flags. Not using an enum to avoid lots of casts.
379 namespace SrgbFlag {
380 constexpr uint32_t kHardwareBuffer = 1 << 0;
381 constexpr uint32_t kSrgbFormat = 1 << 1;
382 constexpr uint32_t kEglColorspaceDefault = 1 << 2;
383 constexpr uint32_t kEglColorspaceLinear = 1 << 3;
384 constexpr uint32_t kEglColorspaceSrgb = 1 << 4;
385 }  // namespace SrgbFlag
386 
configureEglColorspace(EGLint attrs[4],uint32_t srgb_flags)387 static void configureEglColorspace(EGLint attrs[4], uint32_t srgb_flags) {
388     if (srgb_flags & SrgbFlag::kEglColorspaceDefault) {
389         attrs[0] = EGL_GL_COLORSPACE_KHR;
390         attrs[1] = EGL_GL_COLORSPACE_DEFAULT_EXT;
391     } else if (srgb_flags & SrgbFlag::kEglColorspaceLinear) {
392         attrs[0] = EGL_GL_COLORSPACE_KHR;
393         attrs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
394     } else if (srgb_flags & SrgbFlag::kEglColorspaceSrgb) {
395         attrs[0] = EGL_GL_COLORSPACE_KHR;
396         attrs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
397     } else {
398         attrs[0] = EGL_NONE;
399         attrs[1] = EGL_NONE;
400     }
401     attrs[2] = EGL_NONE;
402     attrs[3] = EGL_NONE;
403 }
404 
printSrgbFlags(std::ostream & out,uint32_t srgb_flags)405 static void printSrgbFlags(std::ostream& out, uint32_t srgb_flags) {
406     if (srgb_flags & SrgbFlag::kHardwareBuffer) {
407         out << " AHardwareBuffer";
408     }
409     if (srgb_flags & SrgbFlag::kSrgbFormat) {
410         out << " GL_SRGB_ALPHA";
411     }
412     if (srgb_flags & SrgbFlag::kEglColorspaceDefault) {
413         out << " EGL_GL_COLORSPACE_DEFAULT_KHR";
414     }
415     if (srgb_flags & SrgbFlag::kEglColorspaceLinear) {
416         out << " EGL_GL_COLORSPACE_LINEAR_KHR";
417     }
418     if (srgb_flags & SrgbFlag::kEglColorspaceSrgb) {
419         out << " EGL_GL_COLORSPACE_SRGB_KHR";
420     }
421 }
422 
423 // Draws a gradient and extracts the middle pixel. Returns void to allow ASSERT to work.
testLinearMagnification(JNIEnv * env,uint32_t flags,uint32_t * middle_pixel)424 static void testLinearMagnification(JNIEnv* env, uint32_t flags, uint32_t* middle_pixel) {
425     const bool use_hwbuffer = flags & SrgbFlag::kHardwareBuffer;
426     const bool use_srgb_format = flags & SrgbFlag::kSrgbFormat;
427     GLuint srgbtex;
428     glGenTextures(1, &srgbtex);
429     glBindTexture(GL_TEXTURE_2D, srgbtex);
430     if (use_hwbuffer) {
431         // Create a one-dimensional AHardwareBuffer.
432         AHardwareBuffer_Desc desc = {};
433         desc.width = kTextureWidth;
434         desc.height = 1;
435         desc.layers = 1;
436         desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
437         desc.usage =
438                 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
439         AHardwareBuffer* hwbuffer = nullptr;
440         int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
441         ASSERT_EQ(error, NO_ERROR);
442         // Populate the pixels.
443         uint32_t* pixels = nullptr;
444         error = AHardwareBuffer_lock(hwbuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr,
445                                      reinterpret_cast<void**>(&pixels));
446         ASSERT_EQ(error, NO_ERROR);
447         ASSERT_TRUE(pixels);
448         memcpy(pixels, kTextureData, sizeof(kTextureData));
449         error = AHardwareBuffer_unlock(hwbuffer, nullptr);
450         ASSERT_EQ(error, NO_ERROR);
451         // Create EGLClientBuffer from the AHardwareBuffer.
452         EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
453         ASSERT_TRUE(native_buffer);
454         // Create EGLImage from EGLClientBuffer.
455         EGLint attrs[4];
456         configureEglColorspace(attrs, flags);
457         EGLImageKHR image = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
458                                               EGL_NATIVE_BUFFER_ANDROID, native_buffer, attrs);
459         ASSERT_TRUE(image);
460         // Allocate the OpenGL texture using the EGLImage.
461         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
462     } else {
463         GLenum internal_format = use_srgb_format ? GL_SRGB8_ALPHA8_EXT : GL_RGBA8_OES;
464         GLenum format = use_srgb_format ? GL_SRGB_ALPHA_EXT : GL_RGBA;
465         glTexImage2D(GL_TEXTURE_2D, 0, internal_format, kTextureWidth, 1, 0, format,
466                      GL_UNSIGNED_BYTE, kTextureData);
467     }
468     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
469     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
470     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
471     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
472     ASSERT_EQ(glGetError(), GL_NO_ERROR);
473     // Clear to an interesting constant color to make it easier to spot bugs.
474     glClearColor(1.0, 0.0, 0.5, 0.25);
475     glClear(GL_COLOR_BUFFER_BIT);
476     // Draw the texture.
477     const float kTriangleCoords[] = {-1, -1, -1, 1, 1, -1, 1, 1};
478     glBindTexture(GL_TEXTURE_2D, srgbtex);
479     const int kPositionSlot = 0;
480     glVertexAttribPointer(kPositionSlot, 2, GL_FLOAT, false, 0, kTriangleCoords);
481     glEnableVertexAttribArray(kPositionSlot);
482     glViewport(0, 0, kFramebufferWidth, 1);
483     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
484     // Read back the framebuffer.
485     glReadPixels(kFramebufferWidth / 2, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, middle_pixel);
486     std::ostringstream flag_string;
487     printSrgbFlags(flag_string, flags);
488     LOGV("Filtered Result: %8.8X Flags =%s", *middle_pixel, flag_string.str().c_str());
489     ASSERT_EQ(glGetError(), GL_NO_ERROR);
490 }
491 
492 // Blends a color into an (optionally) sRGB-encoded framebuffer and extracts the final color.
493 // Returns void to allow ASSERT to work.
testFramebufferBlending(JNIEnv * env,uint32_t flags,uint32_t * final_color)494 static void testFramebufferBlending(JNIEnv* env, uint32_t flags, uint32_t* final_color) {
495     const bool use_hwbuffer = flags & SrgbFlag::kHardwareBuffer;
496     const bool use_srgb_format = flags & SrgbFlag::kSrgbFormat;
497     const bool override_egl_colorspace = use_hwbuffer && (flags & SrgbFlag::kEglColorspaceSrgb);
498     GLuint tex;
499     glGenTextures(1, &tex);
500     glBindTexture(GL_TEXTURE_2D, tex);
501     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
502     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
503     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
504     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
505     // Create a 1x1 half-blue, half-opaque texture.
506     const uint32_t kTextureData[] = {
507       kBlendSourceColor,
508     };
509     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
510                  GL_UNSIGNED_BYTE, kTextureData);
511     // Create 1x1 framebuffer object.
512     GLuint fbo;
513     glGenFramebuffers(1, &fbo);
514     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
515     GLuint fbotex;
516     glGenTextures(1, &fbotex);
517     glBindTexture(GL_TEXTURE_2D, fbotex);
518     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
519     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
520     if (use_hwbuffer) {
521         AHardwareBuffer_Desc desc = {};
522         desc.width = 1;
523         desc.height = 1;
524         desc.layers = 1;
525         desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
526         desc.usage =
527                 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
528         AHardwareBuffer* hwbuffer = nullptr;
529         int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
530         ASSERT_EQ(error, NO_ERROR);
531         // Create EGLClientBuffer from the AHardwareBuffer.
532         EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
533         ASSERT_TRUE(native_buffer);
534         // Create EGLImage from EGLClientBuffer.
535         EGLint attrs[4];
536         configureEglColorspace(attrs, flags);
537         EGLImageKHR image = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
538                                               EGL_NATIVE_BUFFER_ANDROID, native_buffer, attrs);
539         ASSERT_TRUE(image);
540         // Allocate the OpenGL texture using the EGLImage.
541         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
542     } else {
543         GLenum internal_format = use_srgb_format ? GL_SRGB8_ALPHA8_EXT : GL_RGBA8_OES;
544         GLenum format = use_srgb_format ? GL_SRGB_ALPHA_EXT : GL_RGBA;
545         glTexImage2D(GL_TEXTURE_2D, 0, internal_format, 1, 1, 0, format,
546                      GL_UNSIGNED_BYTE, nullptr);
547     }
548     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
549                            GL_TEXTURE_2D, fbotex, 0);
550     ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
551     ASSERT_EQ(glGetError(), GL_NO_ERROR);
552     // Clear to half-red.
553     if (use_srgb_format || override_egl_colorspace) {
554         glClearColor(SrgbChannelToLinear(0.5), 0.0, 0.0, 1.0);
555     } else {
556         glClearColor(0.5, 0.0, 0.0, 1.0);
557     }
558     glClear(GL_COLOR_BUFFER_BIT);
559     // Sanity check the cleared color.
560     uint32_t cleared_color = 0;
561     glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &cleared_color);
562     LOGV("  Cleared Color: %8.8X", cleared_color);
563     ASSERT_EQ(cleared_color, kBlendDestColor);
564     // Draw the texture.
565     glEnable(GL_BLEND);
566     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
567     const float kTriangleCoords[] = {-1, -1, -1, 1, 1, -1, 1, 1};
568     glBindTexture(GL_TEXTURE_2D, tex);
569     const int kPositionSlot = 0;
570     glVertexAttribPointer(kPositionSlot, 2, GL_FLOAT, false, 0, kTriangleCoords);
571     glEnableVertexAttribArray(kPositionSlot);
572     glViewport(0, 0, 1, 1);
573     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
574     // Read back the framebuffer.
575     glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, final_color);
576     std::ostringstream flag_string;
577     printSrgbFlags(flag_string, flags);
578     LOGV("Blending Result: %8.8X Flags =%s", *final_color, flag_string.str().c_str());
579     ASSERT_EQ(glGetError(), GL_NO_ERROR);
580 }
581 
582 extern "C" JNIEXPORT void JNICALL
Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestSrgbBuffer(JNIEnv * env,jclass)583 Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestSrgbBuffer(
584     JNIEnv* env, jclass /* unused */) {
585     // First, check the published extension strings against expectations.
586     const char *egl_exts =
587         eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
588     LOGV("EGL Extensions: %s", egl_exts);
589     ASSERT_TRUE(egl_exts);
590     bool egl_colorspace_supported = strstr(egl_exts, "EGL_EXT_image_gl_colorspace");
591     auto gl_exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
592     LOGV("OpenGL Extensions: %s", gl_exts);
593     ASSERT_TRUE(gl_exts);
594     // Load ancillary entry points provided by extensions.
595     LOAD_PROC(eglGetNativeClientBufferANDROID,
596               PFNEGLGETNATIVECLIENTBUFFERANDROID);
597     ASSERT_NE(eglGetNativeClientBufferANDROID, nullptr);
598     LOAD_PROC(eglCreateImageKHR, PFNEGLCREATEIMAGEKHRPROC);
599     ASSERT_NE(eglCreateImageKHR, nullptr);
600     LOAD_PROC(glEGLImageTargetTexture2DOES,
601               PFNGLEGLIMAGETARGETTEXTURE2DOESPROC);
602     ASSERT_NE(glEGLImageTargetTexture2DOES, nullptr);
603     // Create a plain old one-dimensional FBO to render to.
604     GLuint fbo;
605     glGenFramebuffers(1, &fbo);
606     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
607     GLuint fbotex;
608     glGenTextures(1, &fbotex);
609     glBindTexture(GL_TEXTURE_2D, fbotex);
610     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
611     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
612     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kFramebufferWidth, 1, 0, GL_RGBA,
613                  GL_UNSIGNED_BYTE, nullptr);
614     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
615                            GL_TEXTURE_2D, fbotex, 0);
616     ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
617     ASSERT_EQ(glGetError(), GL_NO_ERROR);
618     // Compile and link shaders.
619     int program = glCreateProgram();
620     int vshader = glCreateShader(GL_VERTEX_SHADER);
621     glShaderSource(vshader, 1, &kSrgbVertexCode, nullptr);
622     glCompileShader(vshader);
623     glAttachShader(program, vshader);
624     int fshader = glCreateShader(GL_FRAGMENT_SHADER);
625     glShaderSource(fshader, 1, &kSrgbFragmentCode, nullptr);
626     glCompileShader(fshader);
627     glAttachShader(program, fshader);
628     glLinkProgram(program);
629     int status;
630     glGetProgramiv(program, GL_LINK_STATUS, &status);
631     ASSERT_EQ(status, GL_TRUE);
632     glUseProgram(program);
633     ASSERT_EQ(glGetError(), GL_NO_ERROR);
634 
635     // Filtering test.
636     LOGV("Expected value for NoSrgb = %8.8X", kExpectedMiddlePixel_NoSrgb);
637     LOGV("Expected value for   Srgb = %8.8X", kExpectedMiddlePixel_LinearizeBeforeFiltering);
638     uint32_t middle_pixel;
639     // First do a sanity check with plain old pre-linearized textures.
640     testLinearMagnification(env, 0, &middle_pixel);
641     ASSERT_NEAR(middle_pixel, kExpectedMiddlePixel_NoSrgb, 1);
642     testLinearMagnification(env, SrgbFlag::kHardwareBuffer, &middle_pixel);
643     ASSERT_NEAR(middle_pixel, kExpectedMiddlePixel_NoSrgb, 1);
644     // Try a "normally allocated" OpenGL texture with an sRGB source format.
645     testLinearMagnification(env, SrgbFlag::kSrgbFormat, &middle_pixel);
646     ASSERT_NEAR(middle_pixel, kExpectedMiddlePixel_LinearizeBeforeFiltering, 1);
647     // Try EGL_EXT_image_gl_colorspace.
648     if (egl_colorspace_supported) {
649         testLinearMagnification(env, SrgbFlag::kHardwareBuffer | SrgbFlag::kEglColorspaceDefault, &middle_pixel);
650         ASSERT_NEAR(middle_pixel, kExpectedMiddlePixel_NoSrgb, 1);
651         testLinearMagnification(env, SrgbFlag::kHardwareBuffer | SrgbFlag::kEglColorspaceLinear, &middle_pixel);
652         ASSERT_NEAR(middle_pixel, kExpectedMiddlePixel_NoSrgb, 1);
653         testLinearMagnification(env, SrgbFlag::kHardwareBuffer | SrgbFlag::kEglColorspaceSrgb, &middle_pixel);
654         ASSERT_NEAR(middle_pixel, kExpectedMiddlePixel_LinearizeBeforeFiltering, 1);
655     }
656 
657     // Blending test.
658     LOGV("Expected value for NoSrgb = %8.8X", kExpectedBlendedPixel_NoSrgb);
659     LOGV("Expected value for   Srgb = %8.8X", kExpectedBlendedPixel_Srgb);
660     uint32_t final_color;
661     // First do a sanity check with plain old pre-linearized textures.
662     testFramebufferBlending(env, 0, &final_color);
663     ASSERT_NEAR(final_color, kExpectedBlendedPixel_NoSrgb, 1);
664     testFramebufferBlending(env, SrgbFlag::kHardwareBuffer, &final_color);
665     ASSERT_NEAR(final_color, kExpectedBlendedPixel_NoSrgb, 1);
666     // Try a "normally allocated" OpenGL texture with an sRGB source format.
667     testFramebufferBlending(env, SrgbFlag::kSrgbFormat, &final_color);
668     ASSERT_NEAR(final_color, kExpectedBlendedPixel_Srgb, 1);
669     // Try EGL_EXT_image_gl_colorspace.
670     if (egl_colorspace_supported) {
671         testFramebufferBlending(env, SrgbFlag::kHardwareBuffer | SrgbFlag::kEglColorspaceDefault, &final_color);
672         ASSERT_NEAR(final_color, kExpectedBlendedPixel_NoSrgb, 1);
673         testFramebufferBlending(env, SrgbFlag::kHardwareBuffer | SrgbFlag::kEglColorspaceLinear, &final_color);
674         ASSERT_NEAR(final_color, kExpectedBlendedPixel_NoSrgb, 1);
675         testFramebufferBlending(env, SrgbFlag::kHardwareBuffer | SrgbFlag::kEglColorspaceSrgb, &final_color);
676         ASSERT_NEAR(final_color, kExpectedBlendedPixel_Srgb, 1);
677     }
678 }
679