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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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; 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. 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 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