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