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:
BufferListener(sp<IGraphicBufferConsumer> consumer,BufferCallback callback)41         BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
42               : mConsumer(consumer), mCallback(callback) {}
43 
onFrameAvailable(const BufferItem &)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. */
initialize(uint32_t width,uint32_t height,android_pixel_format_t format,BufferCallback callback)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. */
getSurface() const78     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:
EglManager()94     EglManager()
95           : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
96 
~EglManager()97     ~EglManager() { cleanup(); }
98 
initialize(sp<Surface> surface)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 
makeCurrent() const187     void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); }
188 
present() const189     void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); }
190 
191 private:
cleanup()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:
~Program()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 
initialize(const char * vertex,const char * fragment)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 
use() const256     void use() const { glUseProgram(mProgram); }
257 
bindVec4(GLint location,vec4 v) const258     void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
259 
bindVec3(GLint location,const vec3 * v,uint32_t count) const260     void bindVec3(GLint location, const vec3* v, uint32_t count) const {
261         glUniform3fv(location, count, &(v->x));
262     }
263 
bindFloat(GLint location,float v)264     void bindFloat(GLint location, float v) { glUniform1f(location, v); }
265 
266 private:
buildShader(const char * source,GLenum type) const267     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 
BufferGenerator()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 
~BufferGenerator()317 BufferGenerator::~BufferGenerator() {
318     mEglManager->makeCurrent();
319 }
320 
get(sp<GraphicBuffer> * outBuffer,sp<Fence> * outFence)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
setBuffer(const sp<GraphicBuffer> & buffer,int32_t fence,void * bufferGenerator)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
382