| /* | 
 |  * Copyright 2018 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | // TODO(b/129481165): remove the #pragma below and fix conversion issues | 
 | #pragma clang diagnostic push | 
 | #pragma clang diagnostic ignored "-Wconversion" | 
 |  | 
 | #include <gui/BufferItemConsumer.h> | 
 | #include <gui/Surface.h> | 
 |  | 
 | #include <GLES3/gl3.h> | 
 | #include <math/vec2.h> | 
 | #include <math/vec3.h> | 
 | #include <math/vec4.h> | 
 |  | 
 | #include "BufferGenerator.h" | 
 | #include "BufferGeneratorShader.h" | 
 |  | 
 | namespace android { | 
 |  | 
 | /* Used to receive the surfaces and fences from egl. The egl buffers are thrown | 
 |  * away. The fences are sent to the requester via a callback */ | 
 | class SurfaceManager { | 
 | public: | 
 |     /* Returns a fence from egl */ | 
 |     using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>; | 
 |  | 
 |     /* Listens for a new frame, detaches the buffer and returns the fence | 
 |      * through saved callback. */ | 
 |     class BufferListener : public ConsumerBase::FrameAvailableListener { | 
 |     public: | 
 |         BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback) | 
 |               : mConsumer(consumer), mCallback(callback) {} | 
 |  | 
 |         void onFrameAvailable(const BufferItem& /*item*/) { | 
 |             BufferItem item; | 
 |  | 
 |             if (mConsumer->acquireBuffer(&item, 0)) return; | 
 |             if (mConsumer->detachBuffer(item.mSlot)) return; | 
 |  | 
 |             mCallback(item.mGraphicBuffer, item.mFence->dup()); | 
 |         } | 
 |  | 
 |     private: | 
 |         sp<IGraphicBufferConsumer> mConsumer; | 
 |         BufferCallback mCallback; | 
 |     }; | 
 |  | 
 |     /* Creates a buffer listener that waits on a new frame from the buffer | 
 |      * queue. */ | 
 |     void initialize(uint32_t width, uint32_t height, android_pixel_format_t format, | 
 |                     BufferCallback callback) { | 
 |         sp<IGraphicBufferProducer> producer; | 
 |         sp<IGraphicBufferConsumer> consumer; | 
 |         BufferQueue::createBufferQueue(&producer, &consumer); | 
 |  | 
 |         consumer->setDefaultBufferSize(width, height); | 
 |         consumer->setDefaultBufferFormat(format); | 
 |  | 
 |         mBufferItemConsumer = new BufferItemConsumer(consumer, GraphicBuffer::USAGE_HW_TEXTURE); | 
 |  | 
 |         mListener = new BufferListener(consumer, callback); | 
 |         mBufferItemConsumer->setFrameAvailableListener(mListener); | 
 |  | 
 |         mSurface = new Surface(producer, true); | 
 |     } | 
 |  | 
 |     /* Used by Egl manager. The surface is never displayed. */ | 
 |     sp<Surface> getSurface() const { return mSurface; } | 
 |  | 
 | private: | 
 |     sp<BufferItemConsumer> mBufferItemConsumer; | 
 |     sp<BufferListener> mListener; | 
 |     /* Used by Egl manager. The surface is never displayed */ | 
 |     sp<Surface> mSurface; | 
 | }; | 
 |  | 
 | /* Used to generate valid fences. It is not possible to create a placeholder sync | 
 |  * fence for testing. Egl can generate buffers along with a valid fence. | 
 |  * The buffer cannot be guaranteed to be the same format across all devices so | 
 |  * a CPU filled buffer is used instead. The Egl fence is used along with the | 
 |  * CPU filled buffer. */ | 
 | class EglManager { | 
 | public: | 
 |     EglManager() | 
 |           : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {} | 
 |  | 
 |     ~EglManager() { cleanup(); } | 
 |  | 
 |     int initialize(sp<Surface> surface) { | 
 |         mSurface = surface; | 
 |  | 
 |         mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | 
 |         if (mEglDisplay == EGL_NO_DISPLAY) return false; | 
 |  | 
 |         EGLint major; | 
 |         EGLint minor; | 
 |         if (!eglInitialize(mEglDisplay, &major, &minor)) { | 
 |             ALOGW("Could not initialize EGL"); | 
 |             return false; | 
 |         } | 
 |  | 
 |         /* We're going to use a 1x1 pbuffer surface later on | 
 |          * The configuration distance doesn't really matter for what we're | 
 |          * trying to do */ | 
 |         EGLint configAttrs[] = {EGL_RENDERABLE_TYPE, | 
 |                                 EGL_OPENGL_ES2_BIT, | 
 |                                 EGL_RED_SIZE, | 
 |                                 8, | 
 |                                 EGL_GREEN_SIZE, | 
 |                                 8, | 
 |                                 EGL_BLUE_SIZE, | 
 |                                 8, | 
 |                                 EGL_ALPHA_SIZE, | 
 |                                 0, | 
 |                                 EGL_DEPTH_SIZE, | 
 |                                 24, | 
 |                                 EGL_STENCIL_SIZE, | 
 |                                 0, | 
 |                                 EGL_NONE}; | 
 |  | 
 |         EGLConfig configs[1]; | 
 |         EGLint configCnt; | 
 |         if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1, &configCnt)) { | 
 |             ALOGW("Could not select EGL configuration"); | 
 |             eglReleaseThread(); | 
 |             eglTerminate(mEglDisplay); | 
 |             return false; | 
 |         } | 
 |  | 
 |         if (configCnt <= 0) { | 
 |             ALOGW("Could not find EGL configuration"); | 
 |             eglReleaseThread(); | 
 |             eglTerminate(mEglDisplay); | 
 |             return false; | 
 |         } | 
 |  | 
 |         /* These objects are initialized below but the default "null" values are | 
 |          * used to cleanup properly at any point in the initialization sequence */ | 
 |         EGLint attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; | 
 |         mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT, attrs); | 
 |         if (mEglContext == EGL_NO_CONTEXT) { | 
 |             ALOGW("Could not create EGL context"); | 
 |             cleanup(); | 
 |             return false; | 
 |         } | 
 |  | 
 |         EGLint majorVersion; | 
 |         if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) { | 
 |             ALOGW("Could not query EGL version"); | 
 |             cleanup(); | 
 |             return false; | 
 |         } | 
 |  | 
 |         if (majorVersion != 3) { | 
 |             ALOGW("Unsupported EGL version"); | 
 |             cleanup(); | 
 |             return false; | 
 |         } | 
 |  | 
 |         EGLint surfaceAttrs[] = {EGL_NONE}; | 
 |         mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0], mSurface.get(), surfaceAttrs); | 
 |         if (mEglSurface == EGL_NO_SURFACE) { | 
 |             ALOGW("Could not create EGL surface"); | 
 |             cleanup(); | 
 |             return false; | 
 |         } | 
 |  | 
 |         if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { | 
 |             ALOGW("Could not change current EGL context"); | 
 |             cleanup(); | 
 |             return false; | 
 |         } | 
 |  | 
 |         return true; | 
 |     } | 
 |  | 
 |     void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); } | 
 |  | 
 |     void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); } | 
 |  | 
 | private: | 
 |     void cleanup() { | 
 |         if (mEglDisplay == EGL_NO_DISPLAY) return; | 
 |         if (mEglSurface != EGL_NO_SURFACE) eglDestroySurface(mEglDisplay, mEglSurface); | 
 |         if (mEglContext != EGL_NO_CONTEXT) eglDestroyContext(mEglDisplay, mEglContext); | 
 |  | 
 |         eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | 
 |         eglReleaseThread(); | 
 |         eglTerminate(mEglDisplay); | 
 |     } | 
 |  | 
 |     sp<Surface> mSurface; | 
 |     EGLDisplay mEglDisplay; | 
 |     EGLSurface mEglSurface; | 
 |     EGLContext mEglContext; | 
 | }; | 
 |  | 
 | class Program { | 
 | public: | 
 |     ~Program() { | 
 |         if (mInitialized) { | 
 |             glDetachShader(mProgram, mVertexShader); | 
 |             glDetachShader(mProgram, mFragmentShader); | 
 |  | 
 |             glDeleteShader(mVertexShader); | 
 |             glDeleteShader(mFragmentShader); | 
 |  | 
 |             glDeleteProgram(mProgram); | 
 |         } | 
 |     } | 
 |  | 
 |     bool initialize(const char* vertex, const char* fragment) { | 
 |         mVertexShader = buildShader(vertex, GL_VERTEX_SHADER); | 
 |         if (!mVertexShader) { | 
 |             return false; | 
 |         } | 
 |  | 
 |         mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); | 
 |         if (!mFragmentShader) { | 
 |             return false; | 
 |         } | 
 |  | 
 |         mProgram = glCreateProgram(); | 
 |         glAttachShader(mProgram, mVertexShader); | 
 |         glAttachShader(mProgram, mFragmentShader); | 
 |  | 
 |         glLinkProgram(mProgram); | 
 |  | 
 |         GLint status; | 
 |         glGetProgramiv(mProgram, GL_LINK_STATUS, &status); | 
 |         if (status != GL_TRUE) { | 
 |             GLint length = 0; | 
 |             glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length); | 
 |             if (length > 1) { | 
 |                 GLchar log[length]; | 
 |                 glGetProgramInfoLog(mProgram, length, nullptr, &log[0]); | 
 |                 ALOGE("%s", log); | 
 |             } | 
 |             ALOGE("Error while linking shaders"); | 
 |             return false; | 
 |         } | 
 |         mInitialized = true; | 
 |         return true; | 
 |     } | 
 |  | 
 |     void use() const { glUseProgram(mProgram); } | 
 |  | 
 |     void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); } | 
 |  | 
 |     void bindVec3(GLint location, const vec3* v, uint32_t count) const { | 
 |         glUniform3fv(location, count, &(v->x)); | 
 |     } | 
 |  | 
 |     void bindFloat(GLint location, float v) { glUniform1f(location, v); } | 
 |  | 
 | private: | 
 |     GLuint buildShader(const char* source, GLenum type) const { | 
 |         GLuint shader = glCreateShader(type); | 
 |         glShaderSource(shader, 1, &source, nullptr); | 
 |         glCompileShader(shader); | 
 |  | 
 |         GLint status; | 
 |         glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | 
 |         if (status != GL_TRUE) { | 
 |             ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source); | 
 |             // Some drivers return wrong values for GL_INFO_LOG_LENGTH | 
 |             // use a fixed size instead | 
 |             GLchar log[512]; | 
 |             glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]); | 
 |             ALOGE("Shader info log: %s", log); | 
 |             return 0; | 
 |         } | 
 |  | 
 |         return shader; | 
 |     } | 
 |  | 
 |     GLuint mProgram = 0; | 
 |     GLuint mVertexShader = 0; | 
 |     GLuint mFragmentShader = 0; | 
 |     bool mInitialized = false; | 
 | }; | 
 |  | 
 | BufferGenerator::BufferGenerator() | 
 |       : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) { | 
 |     mBufferSize.set(1000.0, 1000.0); | 
 |  | 
 |     auto setBufferWithContext = | 
 |             std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this); | 
 |     mSurfaceManager->initialize(mBufferSize.width, mBufferSize.height, HAL_PIXEL_FORMAT_RGBA_8888, | 
 |                                 setBufferWithContext); | 
 |  | 
 |     if (!mEglManager->initialize(mSurfaceManager->getSurface())) return; | 
 |  | 
 |     mEglManager->makeCurrent(); | 
 |  | 
 |     if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return; | 
 |     mProgram->use(); | 
 |     mProgram->bindVec4(0, | 
 |                        vec4{mBufferSize.width, mBufferSize.height, 1.0f / mBufferSize.width, | 
 |                             1.0f / mBufferSize.height}); | 
 |     mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4); | 
 |  | 
 |     glEnableVertexAttribArray(0); | 
 |     glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]); | 
 |  | 
 |     mInitialized = true; | 
 | } | 
 |  | 
 | BufferGenerator::~BufferGenerator() { | 
 |     mEglManager->makeCurrent(); | 
 | } | 
 |  | 
 | status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { | 
 |     // mMutex is used to protect get() from getting called by multiple threads at the same time | 
 |     static std::mutex mMutex; | 
 |     std::lock_guard lock(mMutex); | 
 |  | 
 |     if (!mInitialized) { | 
 |         if (outBuffer) { | 
 |             *outBuffer = nullptr; | 
 |         } | 
 |         if (*outFence) { | 
 |             *outFence = nullptr; | 
 |         } | 
 |         return -EINVAL; | 
 |     } | 
 |  | 
 |     // Generate a buffer and fence. They will be returned through the setBuffer callback | 
 |     mEglManager->makeCurrent(); | 
 |  | 
 |     glClear(GL_COLOR_BUFFER_BIT); | 
 |  | 
 |     const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch; | 
 |     mProgram->bindFloat(1, time.count()); | 
 |  | 
 |     glDrawArrays(GL_TRIANGLES, 0, 3); | 
 |  | 
 |     mPending = true; | 
 |     mEglManager->present(); | 
 |  | 
 |     // Wait for the setBuffer callback | 
 |     if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2), | 
 |                                      [this] { return !mPending; })) { | 
 |         ALOGE("failed to set buffer and fence"); | 
 |         return -ETIME; | 
 |     } | 
 |  | 
 |     // Return buffer and fence | 
 |     if (outBuffer) { | 
 |         *outBuffer = mGraphicBuffer; | 
 |     } | 
 |     if (outFence) { | 
 |         *outFence = new Fence(mFence); | 
 |     } else { | 
 |         close(mFence); | 
 |     } | 
 |     mGraphicBuffer = nullptr; | 
 |     mFence = -1; | 
 |  | 
 |     return NO_ERROR; | 
 | } | 
 |  | 
 | ui::Size BufferGenerator::getSize() { | 
 |     return mBufferSize; | 
 | } | 
 |  | 
 | // static | 
 | void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, | 
 |                                 void* bufferGenerator) { | 
 |     BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator); | 
 |     generator->mGraphicBuffer = buffer; | 
 |     generator->mFence = fence; | 
 |     generator->mPending = false; | 
 |     generator->mConditionVariable.notify_all(); | 
 | } | 
 |  | 
 | } // namespace android | 
 |  | 
 | // TODO(b/129481165): remove the #pragma below and fix conversion issues | 
 | #pragma clang diagnostic pop // ignored "-Wconversion" |