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