blob: efab7b856acbb9f909a2abb7ecb5e2406490f4ee [file] [log] [blame]
Marissa Wall713b63f2018-10-17 15:42:43 -07001/*
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
Ady Abrahamb0dbdaa2020-01-06 16:19:42 -080017// TODO(b/129481165): remove the #pragma below and fix conversion issues
18#pragma clang diagnostic push
19#pragma clang diagnostic ignored "-Wconversion"
20
Marissa Wall713b63f2018-10-17 15:42:43 -070021#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
32namespace 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 */
36class SurfaceManager {
37public:
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:
Jim Shargo6ccc5e82024-07-27 03:42:08 +000045#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
46 BufferListener(sp<BufferItemConsumer> consumer, BufferCallback callback)
47#else
Marissa Wall713b63f2018-10-17 15:42:43 -070048 BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
Jim Shargo6ccc5e82024-07-27 03:42:08 +000049#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
50 : mConsumer(consumer), mCallback(callback) {
51 }
Marissa Wall713b63f2018-10-17 15:42:43 -070052
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:
Jim Shargo6ccc5e82024-07-27 03:42:08 +000063#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
64 sp<BufferItemConsumer> mConsumer;
65#else
Marissa Wall713b63f2018-10-17 15:42:43 -070066 sp<IGraphicBufferConsumer> mConsumer;
Jim Shargo6ccc5e82024-07-27 03:42:08 +000067#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
Marissa Wall713b63f2018-10-17 15:42:43 -070068 BufferCallback mCallback;
69 };
70
71 /* Creates a buffer listener that waits on a new frame from the buffer
72 * queue. */
73 void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
74 BufferCallback callback) {
Jim Shargo6ccc5e82024-07-27 03:42:08 +000075#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
Marissa Wall713b63f2018-10-17 15:42:43 -070085 sp<IGraphicBufferProducer> producer;
86 sp<IGraphicBufferConsumer> consumer;
87 BufferQueue::createBufferQueue(&producer, &consumer);
88
89 consumer->setDefaultBufferSize(width, height);
90 consumer->setDefaultBufferFormat(format);
91
Ady Abrahamd11bade2022-08-01 16:18:03 -070092 mBufferItemConsumer =
93 sp<BufferItemConsumer>::make(consumer, GraphicBuffer::USAGE_HW_TEXTURE);
Marissa Wall713b63f2018-10-17 15:42:43 -070094
Ady Abrahamd11bade2022-08-01 16:18:03 -070095 mListener = sp<BufferListener>::make(consumer, callback);
Marissa Wall713b63f2018-10-17 15:42:43 -070096 mBufferItemConsumer->setFrameAvailableListener(mListener);
97
Ady Abrahamd11bade2022-08-01 16:18:03 -070098 mSurface = sp<Surface>::make(producer, true);
Jim Shargo6ccc5e82024-07-27 03:42:08 +000099#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
Marissa Wall713b63f2018-10-17 15:42:43 -0700100 }
101
102 /* Used by Egl manager. The surface is never displayed. */
103 sp<Surface> getSurface() const { return mSurface; }
104
105private:
106 sp<BufferItemConsumer> mBufferItemConsumer;
107 sp<BufferListener> mListener;
108 /* Used by Egl manager. The surface is never displayed */
109 sp<Surface> mSurface;
110};
111
Peiyong Lind8460c82020-07-28 16:04:22 -0700112/* Used to generate valid fences. It is not possible to create a placeholder sync
Marissa Wall713b63f2018-10-17 15:42:43 -0700113 * 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. */
117class EglManager {
118public:
119 EglManager()
120 : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
121
122 ~EglManager() { cleanup(); }
123
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
212 void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); }
213
214 void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); }
215
216private:
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
233class Program {
234public:
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
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
281 void use() const { glUseProgram(mProgram); }
282
283 void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
284
285 void bindVec3(GLint location, const vec3* v, uint32_t count) const {
286 glUniform3fv(location, count, &(v->x));
287 }
288
289 void bindFloat(GLint location, float v) { glUniform1f(location, v); }
290
291private:
292 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
318BufferGenerator::BufferGenerator()
319 : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
Chavi Weingartena5aedbd2021-04-09 13:37:33 +0000320 mBufferSize.set(1000.0, 1000.0);
Marissa Wall713b63f2018-10-17 15:42:43 -0700321
322 auto setBufferWithContext =
323 std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
Chavi Weingartena5aedbd2021-04-09 13:37:33 +0000324 mSurfaceManager->initialize(mBufferSize.width, mBufferSize.height, HAL_PIXEL_FORMAT_RGBA_8888,
325 setBufferWithContext);
Marissa Wall713b63f2018-10-17 15:42:43 -0700326
327 if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
328
329 mEglManager->makeCurrent();
330
331 if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
332 mProgram->use();
Chavi Weingartena5aedbd2021-04-09 13:37:33 +0000333 mProgram->bindVec4(0,
334 vec4{mBufferSize.width, mBufferSize.height, 1.0f / mBufferSize.width,
335 1.0f / mBufferSize.height});
Marissa Wall713b63f2018-10-17 15:42:43 -0700336 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
344BufferGenerator::~BufferGenerator() {
345 mEglManager->makeCurrent();
346}
347
348status_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) {
Ady Abrahamd11bade2022-08-01 16:18:03 -0700388 *outFence = sp<Fence>::make(mFence);
Marissa Wall713b63f2018-10-17 15:42:43 -0700389 } else {
390 close(mFence);
391 }
392 mGraphicBuffer = nullptr;
393 mFence = -1;
394
395 return NO_ERROR;
396}
397
Chavi Weingartena5aedbd2021-04-09 13:37:33 +0000398ui::Size BufferGenerator::getSize() {
399 return mBufferSize;
400}
401
Marissa Wall713b63f2018-10-17 15:42:43 -0700402// static
403void 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
Ady Abrahamb0dbdaa2020-01-06 16:19:42 -0800413
414// TODO(b/129481165): remove the #pragma below and fix conversion issues
415#pragma clang diagnostic pop // ignored "-Wconversion"