| Marissa Wall | 713b63f | 2018-10-17 15:42:43 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2018 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 <gui/BufferItemConsumer.h> | 
 | 18 | #include <gui/Surface.h> | 
 | 19 |  | 
 | 20 | #include <GLES3/gl3.h> | 
 | 21 | #include <math/vec2.h> | 
 | 22 | #include <math/vec3.h> | 
 | 23 | #include <math/vec4.h> | 
 | 24 |  | 
 | 25 | #include "BufferGenerator.h" | 
 | 26 | #include "BufferGeneratorShader.h" | 
 | 27 |  | 
 | 28 | namespace android { | 
 | 29 |  | 
 | 30 | /* Used to receive the surfaces and fences from egl. The egl buffers are thrown | 
 | 31 |  * away. The fences are sent to the requester via a callback */ | 
 | 32 | class SurfaceManager { | 
 | 33 | public: | 
 | 34 |     /* Returns a fence from egl */ | 
 | 35 |     using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>; | 
 | 36 |  | 
 | 37 |     /* Listens for a new frame, detaches the buffer and returns the fence | 
 | 38 |      * through saved callback. */ | 
 | 39 |     class BufferListener : public ConsumerBase::FrameAvailableListener { | 
 | 40 |     public: | 
 | 41 |         BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback) | 
 | 42 |               : mConsumer(consumer), mCallback(callback) {} | 
 | 43 |  | 
 | 44 |         void onFrameAvailable(const BufferItem& /*item*/) { | 
 | 45 |             BufferItem item; | 
 | 46 |  | 
 | 47 |             if (mConsumer->acquireBuffer(&item, 0)) return; | 
 | 48 |             if (mConsumer->detachBuffer(item.mSlot)) return; | 
 | 49 |  | 
 | 50 |             mCallback(item.mGraphicBuffer, item.mFence->dup()); | 
 | 51 |         } | 
 | 52 |  | 
 | 53 |     private: | 
 | 54 |         sp<IGraphicBufferConsumer> mConsumer; | 
 | 55 |         BufferCallback mCallback; | 
 | 56 |     }; | 
 | 57 |  | 
 | 58 |     /* Creates a buffer listener that waits on a new frame from the buffer | 
 | 59 |      * queue. */ | 
 | 60 |     void initialize(uint32_t width, uint32_t height, android_pixel_format_t format, | 
 | 61 |                     BufferCallback callback) { | 
 | 62 |         sp<IGraphicBufferProducer> producer; | 
 | 63 |         sp<IGraphicBufferConsumer> consumer; | 
 | 64 |         BufferQueue::createBufferQueue(&producer, &consumer); | 
 | 65 |  | 
 | 66 |         consumer->setDefaultBufferSize(width, height); | 
 | 67 |         consumer->setDefaultBufferFormat(format); | 
 | 68 |  | 
 | 69 |         mBufferItemConsumer = new BufferItemConsumer(consumer, 0); | 
 | 70 |  | 
 | 71 |         mListener = new BufferListener(consumer, callback); | 
 | 72 |         mBufferItemConsumer->setFrameAvailableListener(mListener); | 
 | 73 |  | 
 | 74 |         mSurface = new Surface(producer, true); | 
 | 75 |     } | 
 | 76 |  | 
 | 77 |     /* Used by Egl manager. The surface is never displayed. */ | 
 | 78 |     sp<Surface> getSurface() const { return mSurface; } | 
 | 79 |  | 
 | 80 | private: | 
 | 81 |     sp<BufferItemConsumer> mBufferItemConsumer; | 
 | 82 |     sp<BufferListener> mListener; | 
 | 83 |     /* Used by Egl manager. The surface is never displayed */ | 
 | 84 |     sp<Surface> mSurface; | 
 | 85 | }; | 
 | 86 |  | 
 | 87 | /* Used to generate valid fences. It is not possible to create a dummy sync | 
 | 88 |  * fence for testing. Egl can generate buffers along with a valid fence. | 
 | 89 |  * The buffer cannot be guaranteed to be the same format across all devices so | 
 | 90 |  * a CPU filled buffer is used instead. The Egl fence is used along with the | 
 | 91 |  * CPU filled buffer. */ | 
 | 92 | class EglManager { | 
 | 93 | public: | 
 | 94 |     EglManager() | 
 | 95 |           : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {} | 
 | 96 |  | 
 | 97 |     ~EglManager() { cleanup(); } | 
 | 98 |  | 
 | 99 |     int initialize(sp<Surface> surface) { | 
 | 100 |         mSurface = surface; | 
 | 101 |  | 
 | 102 |         mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | 
 | 103 |         if (mEglDisplay == EGL_NO_DISPLAY) return false; | 
 | 104 |  | 
 | 105 |         EGLint major; | 
 | 106 |         EGLint minor; | 
 | 107 |         if (!eglInitialize(mEglDisplay, &major, &minor)) { | 
 | 108 |             ALOGW("Could not initialize EGL"); | 
 | 109 |             return false; | 
 | 110 |         } | 
 | 111 |  | 
 | 112 |         /* We're going to use a 1x1 pbuffer surface later on | 
 | 113 |          * The configuration distance doesn't really matter for what we're | 
 | 114 |          * trying to do */ | 
 | 115 |         EGLint configAttrs[] = {EGL_RENDERABLE_TYPE, | 
 | 116 |                                 EGL_OPENGL_ES2_BIT, | 
 | 117 |                                 EGL_RED_SIZE, | 
 | 118 |                                 8, | 
 | 119 |                                 EGL_GREEN_SIZE, | 
 | 120 |                                 8, | 
 | 121 |                                 EGL_BLUE_SIZE, | 
 | 122 |                                 8, | 
 | 123 |                                 EGL_ALPHA_SIZE, | 
 | 124 |                                 0, | 
 | 125 |                                 EGL_DEPTH_SIZE, | 
 | 126 |                                 24, | 
 | 127 |                                 EGL_STENCIL_SIZE, | 
 | 128 |                                 0, | 
 | 129 |                                 EGL_NONE}; | 
 | 130 |  | 
 | 131 |         EGLConfig configs[1]; | 
 | 132 |         EGLint configCnt; | 
 | 133 |         if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1, &configCnt)) { | 
 | 134 |             ALOGW("Could not select EGL configuration"); | 
 | 135 |             eglReleaseThread(); | 
 | 136 |             eglTerminate(mEglDisplay); | 
 | 137 |             return false; | 
 | 138 |         } | 
 | 139 |  | 
 | 140 |         if (configCnt <= 0) { | 
 | 141 |             ALOGW("Could not find EGL configuration"); | 
 | 142 |             eglReleaseThread(); | 
 | 143 |             eglTerminate(mEglDisplay); | 
 | 144 |             return false; | 
 | 145 |         } | 
 | 146 |  | 
 | 147 |         /* These objects are initialized below but the default "null" values are | 
 | 148 |          * used to cleanup properly at any point in the initialization sequence */ | 
 | 149 |         EGLint attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; | 
 | 150 |         mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT, attrs); | 
 | 151 |         if (mEglContext == EGL_NO_CONTEXT) { | 
 | 152 |             ALOGW("Could not create EGL context"); | 
 | 153 |             cleanup(); | 
 | 154 |             return false; | 
 | 155 |         } | 
 | 156 |  | 
 | 157 |         EGLint majorVersion; | 
 | 158 |         if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) { | 
 | 159 |             ALOGW("Could not query EGL version"); | 
 | 160 |             cleanup(); | 
 | 161 |             return false; | 
 | 162 |         } | 
 | 163 |  | 
 | 164 |         if (majorVersion != 3) { | 
 | 165 |             ALOGW("Unsupported EGL version"); | 
 | 166 |             cleanup(); | 
 | 167 |             return false; | 
 | 168 |         } | 
 | 169 |  | 
 | 170 |         EGLint surfaceAttrs[] = {EGL_NONE}; | 
 | 171 |         mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0], mSurface.get(), surfaceAttrs); | 
 | 172 |         if (mEglSurface == EGL_NO_SURFACE) { | 
 | 173 |             ALOGW("Could not create EGL surface"); | 
 | 174 |             cleanup(); | 
 | 175 |             return false; | 
 | 176 |         } | 
 | 177 |  | 
 | 178 |         if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { | 
 | 179 |             ALOGW("Could not change current EGL context"); | 
 | 180 |             cleanup(); | 
 | 181 |             return false; | 
 | 182 |         } | 
 | 183 |  | 
 | 184 |         return true; | 
 | 185 |     } | 
 | 186 |  | 
 | 187 |     void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); } | 
 | 188 |  | 
 | 189 |     void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); } | 
 | 190 |  | 
 | 191 | private: | 
 | 192 |     void cleanup() { | 
 | 193 |         if (mEglDisplay == EGL_NO_DISPLAY) return; | 
 | 194 |         if (mEglSurface != EGL_NO_SURFACE) eglDestroySurface(mEglDisplay, mEglSurface); | 
 | 195 |         if (mEglContext != EGL_NO_CONTEXT) eglDestroyContext(mEglDisplay, mEglContext); | 
 | 196 |  | 
 | 197 |         eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | 
 | 198 |         eglReleaseThread(); | 
 | 199 |         eglTerminate(mEglDisplay); | 
 | 200 |     } | 
 | 201 |  | 
 | 202 |     sp<Surface> mSurface; | 
 | 203 |     EGLDisplay mEglDisplay; | 
 | 204 |     EGLSurface mEglSurface; | 
 | 205 |     EGLContext mEglContext; | 
 | 206 | }; | 
 | 207 |  | 
 | 208 | class Program { | 
 | 209 | public: | 
 | 210 |     ~Program() { | 
 | 211 |         if (mInitialized) { | 
 | 212 |             glDetachShader(mProgram, mVertexShader); | 
 | 213 |             glDetachShader(mProgram, mFragmentShader); | 
 | 214 |  | 
 | 215 |             glDeleteShader(mVertexShader); | 
 | 216 |             glDeleteShader(mFragmentShader); | 
 | 217 |  | 
 | 218 |             glDeleteProgram(mProgram); | 
 | 219 |         } | 
 | 220 |     } | 
 | 221 |  | 
 | 222 |     bool initialize(const char* vertex, const char* fragment) { | 
 | 223 |         mVertexShader = buildShader(vertex, GL_VERTEX_SHADER); | 
 | 224 |         if (!mVertexShader) { | 
 | 225 |             return false; | 
 | 226 |         } | 
 | 227 |  | 
 | 228 |         mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); | 
 | 229 |         if (!mFragmentShader) { | 
 | 230 |             return false; | 
 | 231 |         } | 
 | 232 |  | 
 | 233 |         mProgram = glCreateProgram(); | 
 | 234 |         glAttachShader(mProgram, mVertexShader); | 
 | 235 |         glAttachShader(mProgram, mFragmentShader); | 
 | 236 |  | 
 | 237 |         glLinkProgram(mProgram); | 
 | 238 |  | 
 | 239 |         GLint status; | 
 | 240 |         glGetProgramiv(mProgram, GL_LINK_STATUS, &status); | 
 | 241 |         if (status != GL_TRUE) { | 
 | 242 |             GLint length = 0; | 
 | 243 |             glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length); | 
 | 244 |             if (length > 1) { | 
 | 245 |                 GLchar log[length]; | 
 | 246 |                 glGetProgramInfoLog(mProgram, length, nullptr, &log[0]); | 
 | 247 |                 ALOGE("%s", log); | 
 | 248 |             } | 
 | 249 |             ALOGE("Error while linking shaders"); | 
 | 250 |             return false; | 
 | 251 |         } | 
 | 252 |         mInitialized = true; | 
 | 253 |         return true; | 
 | 254 |     } | 
 | 255 |  | 
 | 256 |     void use() const { glUseProgram(mProgram); } | 
 | 257 |  | 
 | 258 |     void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); } | 
 | 259 |  | 
 | 260 |     void bindVec3(GLint location, const vec3* v, uint32_t count) const { | 
 | 261 |         glUniform3fv(location, count, &(v->x)); | 
 | 262 |     } | 
 | 263 |  | 
 | 264 |     void bindFloat(GLint location, float v) { glUniform1f(location, v); } | 
 | 265 |  | 
 | 266 | private: | 
 | 267 |     GLuint buildShader(const char* source, GLenum type) const { | 
 | 268 |         GLuint shader = glCreateShader(type); | 
 | 269 |         glShaderSource(shader, 1, &source, nullptr); | 
 | 270 |         glCompileShader(shader); | 
 | 271 |  | 
 | 272 |         GLint status; | 
 | 273 |         glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | 
 | 274 |         if (status != GL_TRUE) { | 
 | 275 |             ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source); | 
 | 276 |             // Some drivers return wrong values for GL_INFO_LOG_LENGTH | 
 | 277 |             // use a fixed size instead | 
 | 278 |             GLchar log[512]; | 
 | 279 |             glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]); | 
 | 280 |             ALOGE("Shader info log: %s", log); | 
 | 281 |             return 0; | 
 | 282 |         } | 
 | 283 |  | 
 | 284 |         return shader; | 
 | 285 |     } | 
 | 286 |  | 
 | 287 |     GLuint mProgram = 0; | 
 | 288 |     GLuint mVertexShader = 0; | 
 | 289 |     GLuint mFragmentShader = 0; | 
 | 290 |     bool mInitialized = false; | 
 | 291 | }; | 
 | 292 |  | 
 | 293 | BufferGenerator::BufferGenerator() | 
 | 294 |       : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) { | 
 | 295 |     const float width = 1000.0; | 
 | 296 |     const float height = 1000.0; | 
 | 297 |  | 
 | 298 |     auto setBufferWithContext = | 
 | 299 |             std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this); | 
 | 300 |     mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext); | 
 | 301 |  | 
 | 302 |     if (!mEglManager->initialize(mSurfaceManager->getSurface())) return; | 
 | 303 |  | 
 | 304 |     mEglManager->makeCurrent(); | 
 | 305 |  | 
 | 306 |     if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return; | 
 | 307 |     mProgram->use(); | 
 | 308 |     mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height}); | 
 | 309 |     mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4); | 
 | 310 |  | 
 | 311 |     glEnableVertexAttribArray(0); | 
 | 312 |     glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]); | 
 | 313 |  | 
 | 314 |     mInitialized = true; | 
 | 315 | } | 
 | 316 |  | 
 | 317 | BufferGenerator::~BufferGenerator() { | 
 | 318 |     mEglManager->makeCurrent(); | 
 | 319 | } | 
 | 320 |  | 
 | 321 | status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { | 
 | 322 |     // mMutex is used to protect get() from getting called by multiple threads at the same time | 
 | 323 |     static std::mutex mMutex; | 
 | 324 |     std::lock_guard lock(mMutex); | 
 | 325 |  | 
 | 326 |     if (!mInitialized) { | 
 | 327 |         if (outBuffer) { | 
 | 328 |             *outBuffer = nullptr; | 
 | 329 |         } | 
 | 330 |         if (*outFence) { | 
 | 331 |             *outFence = nullptr; | 
 | 332 |         } | 
 | 333 |         return -EINVAL; | 
 | 334 |     } | 
 | 335 |  | 
 | 336 |     // Generate a buffer and fence. They will be returned through the setBuffer callback | 
 | 337 |     mEglManager->makeCurrent(); | 
 | 338 |  | 
 | 339 |     glClear(GL_COLOR_BUFFER_BIT); | 
 | 340 |  | 
 | 341 |     const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch; | 
 | 342 |     mProgram->bindFloat(1, time.count()); | 
 | 343 |  | 
 | 344 |     glDrawArrays(GL_TRIANGLES, 0, 3); | 
 | 345 |  | 
 | 346 |     mPending = true; | 
 | 347 |     mEglManager->present(); | 
 | 348 |  | 
 | 349 |     // Wait for the setBuffer callback | 
 | 350 |     if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2), | 
 | 351 |                                      [this] { return !mPending; })) { | 
 | 352 |         ALOGE("failed to set buffer and fence"); | 
 | 353 |         return -ETIME; | 
 | 354 |     } | 
 | 355 |  | 
 | 356 |     // Return buffer and fence | 
 | 357 |     if (outBuffer) { | 
 | 358 |         *outBuffer = mGraphicBuffer; | 
 | 359 |     } | 
 | 360 |     if (outFence) { | 
 | 361 |         *outFence = new Fence(mFence); | 
 | 362 |     } else { | 
 | 363 |         close(mFence); | 
 | 364 |     } | 
 | 365 |     mGraphicBuffer = nullptr; | 
 | 366 |     mFence = -1; | 
 | 367 |  | 
 | 368 |     return NO_ERROR; | 
 | 369 | } | 
 | 370 |  | 
 | 371 | // static | 
 | 372 | void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, | 
 | 373 |                                 void* bufferGenerator) { | 
 | 374 |     BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator); | 
 | 375 |     generator->mGraphicBuffer = buffer; | 
 | 376 |     generator->mFence = fence; | 
 | 377 |     generator->mPending = false; | 
 | 378 |     generator->mConditionVariable.notify_all(); | 
 | 379 | } | 
 | 380 |  | 
 | 381 | } // namespace android |