123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- /*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <gui/BufferItemConsumer.h>
- #include <gui/Surface.h>
- #include <GLES3/gl3.h>
- #include <math/vec2.h>
- #include <math/vec3.h>
- #include <math/vec4.h>
- #include "BufferGenerator.h"
- #include "BufferGeneratorShader.h"
- namespace android {
- /* Used to receive the surfaces and fences from egl. The egl buffers are thrown
- * away. The fences are sent to the requester via a callback */
- class SurfaceManager {
- public:
- /* Returns a fence from egl */
- using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>;
- /* Listens for a new frame, detaches the buffer and returns the fence
- * through saved callback. */
- class BufferListener : public ConsumerBase::FrameAvailableListener {
- public:
- BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
- : mConsumer(consumer), mCallback(callback) {}
- void onFrameAvailable(const BufferItem& /*item*/) {
- BufferItem item;
- if (mConsumer->acquireBuffer(&item, 0)) return;
- if (mConsumer->detachBuffer(item.mSlot)) return;
- mCallback(item.mGraphicBuffer, item.mFence->dup());
- }
- private:
- sp<IGraphicBufferConsumer> mConsumer;
- BufferCallback mCallback;
- };
- /* Creates a buffer listener that waits on a new frame from the buffer
- * queue. */
- void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
- BufferCallback callback) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- consumer->setDefaultBufferSize(width, height);
- consumer->setDefaultBufferFormat(format);
- mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
- mListener = new BufferListener(consumer, callback);
- mBufferItemConsumer->setFrameAvailableListener(mListener);
- mSurface = new Surface(producer, true);
- }
- /* Used by Egl manager. The surface is never displayed. */
- sp<Surface> getSurface() const { return mSurface; }
- private:
- sp<BufferItemConsumer> mBufferItemConsumer;
- sp<BufferListener> mListener;
- /* Used by Egl manager. The surface is never displayed */
- sp<Surface> mSurface;
- };
- /* Used to generate valid fences. It is not possible to create a dummy sync
- * fence for testing. Egl can generate buffers along with a valid fence.
- * The buffer cannot be guaranteed to be the same format across all devices so
- * a CPU filled buffer is used instead. The Egl fence is used along with the
- * CPU filled buffer. */
- class EglManager {
- public:
- EglManager()
- : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
- ~EglManager() { cleanup(); }
- int initialize(sp<Surface> surface) {
- mSurface = surface;
- mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (mEglDisplay == EGL_NO_DISPLAY) return false;
- EGLint major;
- EGLint minor;
- if (!eglInitialize(mEglDisplay, &major, &minor)) {
- ALOGW("Could not initialize EGL");
- return false;
- }
- /* We're going to use a 1x1 pbuffer surface later on
- * The configuration distance doesn't really matter for what we're
- * trying to do */
- EGLint configAttrs[] = {EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE,
- 8,
- EGL_GREEN_SIZE,
- 8,
- EGL_BLUE_SIZE,
- 8,
- EGL_ALPHA_SIZE,
- 0,
- EGL_DEPTH_SIZE,
- 24,
- EGL_STENCIL_SIZE,
- 0,
- EGL_NONE};
- EGLConfig configs[1];
- EGLint configCnt;
- if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1, &configCnt)) {
- ALOGW("Could not select EGL configuration");
- eglReleaseThread();
- eglTerminate(mEglDisplay);
- return false;
- }
- if (configCnt <= 0) {
- ALOGW("Could not find EGL configuration");
- eglReleaseThread();
- eglTerminate(mEglDisplay);
- return false;
- }
- /* These objects are initialized below but the default "null" values are
- * used to cleanup properly at any point in the initialization sequence */
- EGLint attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
- mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT, attrs);
- if (mEglContext == EGL_NO_CONTEXT) {
- ALOGW("Could not create EGL context");
- cleanup();
- return false;
- }
- EGLint majorVersion;
- if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) {
- ALOGW("Could not query EGL version");
- cleanup();
- return false;
- }
- if (majorVersion != 3) {
- ALOGW("Unsupported EGL version");
- cleanup();
- return false;
- }
- EGLint surfaceAttrs[] = {EGL_NONE};
- mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0], mSurface.get(), surfaceAttrs);
- if (mEglSurface == EGL_NO_SURFACE) {
- ALOGW("Could not create EGL surface");
- cleanup();
- return false;
- }
- if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- ALOGW("Could not change current EGL context");
- cleanup();
- return false;
- }
- return true;
- }
- void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); }
- void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); }
- private:
- void cleanup() {
- if (mEglDisplay == EGL_NO_DISPLAY) return;
- if (mEglSurface != EGL_NO_SURFACE) eglDestroySurface(mEglDisplay, mEglSurface);
- if (mEglContext != EGL_NO_CONTEXT) eglDestroyContext(mEglDisplay, mEglContext);
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglReleaseThread();
- eglTerminate(mEglDisplay);
- }
- sp<Surface> mSurface;
- EGLDisplay mEglDisplay;
- EGLSurface mEglSurface;
- EGLContext mEglContext;
- };
- class Program {
- public:
- ~Program() {
- if (mInitialized) {
- glDetachShader(mProgram, mVertexShader);
- glDetachShader(mProgram, mFragmentShader);
- glDeleteShader(mVertexShader);
- glDeleteShader(mFragmentShader);
- glDeleteProgram(mProgram);
- }
- }
- bool initialize(const char* vertex, const char* fragment) {
- mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
- if (!mVertexShader) {
- return false;
- }
- mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
- if (!mFragmentShader) {
- return false;
- }
- mProgram = glCreateProgram();
- glAttachShader(mProgram, mVertexShader);
- glAttachShader(mProgram, mFragmentShader);
- glLinkProgram(mProgram);
- GLint status;
- glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
- if (status != GL_TRUE) {
- GLint length = 0;
- glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);
- if (length > 1) {
- GLchar log[length];
- glGetProgramInfoLog(mProgram, length, nullptr, &log[0]);
- ALOGE("%s", log);
- }
- ALOGE("Error while linking shaders");
- return false;
- }
- mInitialized = true;
- return true;
- }
- void use() const { glUseProgram(mProgram); }
- void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
- void bindVec3(GLint location, const vec3* v, uint32_t count) const {
- glUniform3fv(location, count, &(v->x));
- }
- void bindFloat(GLint location, float v) { glUniform1f(location, v); }
- private:
- GLuint buildShader(const char* source, GLenum type) const {
- GLuint shader = glCreateShader(type);
- glShaderSource(shader, 1, &source, nullptr);
- glCompileShader(shader);
- GLint status;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (status != GL_TRUE) {
- ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source);
- // Some drivers return wrong values for GL_INFO_LOG_LENGTH
- // use a fixed size instead
- GLchar log[512];
- glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
- ALOGE("Shader info log: %s", log);
- return 0;
- }
- return shader;
- }
- GLuint mProgram = 0;
- GLuint mVertexShader = 0;
- GLuint mFragmentShader = 0;
- bool mInitialized = false;
- };
- BufferGenerator::BufferGenerator()
- : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
- const float width = 1000.0;
- const float height = 1000.0;
- auto setBufferWithContext =
- std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
- mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
- if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
- mEglManager->makeCurrent();
- if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
- mProgram->use();
- mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
- mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]);
- mInitialized = true;
- }
- BufferGenerator::~BufferGenerator() {
- mEglManager->makeCurrent();
- }
- status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
- // mMutex is used to protect get() from getting called by multiple threads at the same time
- static std::mutex mMutex;
- std::lock_guard lock(mMutex);
- if (!mInitialized) {
- if (outBuffer) {
- *outBuffer = nullptr;
- }
- if (*outFence) {
- *outFence = nullptr;
- }
- return -EINVAL;
- }
- // Generate a buffer and fence. They will be returned through the setBuffer callback
- mEglManager->makeCurrent();
- glClear(GL_COLOR_BUFFER_BIT);
- const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch;
- mProgram->bindFloat(1, time.count());
- glDrawArrays(GL_TRIANGLES, 0, 3);
- mPending = true;
- mEglManager->present();
- // Wait for the setBuffer callback
- if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2),
- [this] { return !mPending; })) {
- ALOGE("failed to set buffer and fence");
- return -ETIME;
- }
- // Return buffer and fence
- if (outBuffer) {
- *outBuffer = mGraphicBuffer;
- }
- if (outFence) {
- *outFence = new Fence(mFence);
- } else {
- close(mFence);
- }
- mGraphicBuffer = nullptr;
- mFence = -1;
- return NO_ERROR;
- }
- // static
- void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
- void* bufferGenerator) {
- BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator);
- generator->mGraphicBuffer = buffer;
- generator->mFence = fence;
- generator->mPending = false;
- generator->mConditionVariable.notify_all();
- }
- } // namespace android
|