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