xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/tests/BufferGenerator.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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