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