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