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