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