/* * Copyright (C) 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. */ #pragma once #include #include #include #include #include #include namespace android { class SurfaceTexture; /* * EGLConsumer implements the parts of SurfaceTexture that deal with * textures attached to an GL context. */ class EGLConsumer { public: EGLConsumer(); /** * updateTexImage acquires the most recently queued buffer, and sets the * image contents of the target texture to it. * * This call may only be made while the OpenGL ES context to which the * target texture belongs is bound to the calling thread. * * This calls doGLFenceWait to ensure proper synchronization. */ status_t updateTexImage(SurfaceTexture& st); /* * releaseTexImage releases the texture acquired in updateTexImage(). * This is intended to be used in single buffer mode. * * This call may only be made while the OpenGL ES context to which the * target texture belongs is bound to the calling thread. */ status_t releaseTexImage(SurfaceTexture& st); /** * detachFromContext detaches the EGLConsumer from the calling thread's * current OpenGL ES context. This context must be the same as the context * that was current for previous calls to updateTexImage. * * Detaching a EGLConsumer from an OpenGL ES context will result in the * deletion of the OpenGL ES texture object into which the images were being * streamed. After a EGLConsumer has been detached from the OpenGL ES * context calls to updateTexImage will fail returning INVALID_OPERATION * until the EGLConsumer is attached to a new OpenGL ES context using the * attachToContext method. */ status_t detachFromContext(SurfaceTexture& st); /** * attachToContext attaches a EGLConsumer that is currently in the * 'detached' state to the current OpenGL ES context. A EGLConsumer is * in the 'detached' state iff detachFromContext has successfully been * called and no calls to attachToContext have succeeded since the last * detachFromContext call. Calls to attachToContext made on a * EGLConsumer that is not in the 'detached' state will result in an * INVALID_OPERATION error. * * The tex argument specifies the OpenGL ES texture object name in the * new context into which the image contents will be streamed. A successful * call to attachToContext will result in this texture object being bound to * the texture target and populated with the image contents that were * current at the time of the last call to detachFromContext. */ status_t attachToContext(uint32_t tex, SurfaceTexture& st); /** * onAcquireBufferLocked amends the ConsumerBase method to update the * mEglSlots array in addition to the ConsumerBase behavior. */ void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); /** * onReleaseBufferLocked amends the ConsumerBase method to update the * mEglSlots array in addition to the ConsumerBase. */ void onReleaseBufferLocked(int slot); /** * onFreeBufferLocked frees up the given buffer slot. If the slot has been * initialized this will release the reference to the GraphicBuffer in that * slot and destroy the EGLImage in that slot. Otherwise it has no effect. */ void onFreeBufferLocked(int slotIndex); /** * onAbandonLocked amends the ConsumerBase method to clear * mCurrentTextureImage in addition to the ConsumerBase behavior. */ void onAbandonLocked(); protected: struct PendingRelease { PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer(), display(nullptr), fence(nullptr) {} bool isPending; int currentTexture; sp graphicBuffer; EGLDisplay display; EGLSyncKHR fence; }; /** * This releases the buffer in the slot referenced by mCurrentTexture, * then updates state to refer to the BufferItem, which must be a * newly-acquired buffer. If pendingRelease is not null, the parameters * which would have been passed to releaseBufferLocked upon the successful * completion of the method will instead be returned to the caller, so that * it may call releaseBufferLocked itself later. */ status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, SurfaceTexture& st); /** * Binds mTexName and the current buffer to mTexTarget. Uses * mCurrentTexture if it's set, mCurrentTextureImage if not. If the * bind succeeds, this calls doGLFenceWait. */ status_t bindTextureImageLocked(SurfaceTexture& st); /** * Gets the current EGLDisplay and EGLContext values, and compares them * to mEglDisplay and mEglContext. If the fields have been previously * set, the values must match; if not, the fields are set to the current * values. * The contextCheck argument is used to ensure that a GL context is * properly set; when set to false, the check is not performed. */ status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); /** * EglImage is a utility class for tracking and creating EGLImageKHRs. There * is primarily just one image per slot, but there is also special cases: * - For releaseTexImage, we use a debug image (mReleasedTexImage) * - After freeBuffer, we must still keep the current image/buffer * Reference counting EGLImages lets us handle all these cases easily while * also only creating new EGLImages from buffers when required. */ class EglImage : public LightRefBase { public: EglImage(sp graphicBuffer); /** * createIfNeeded creates an EGLImage if required (we haven't created * one yet, or the EGLDisplay or crop-rect has changed). */ status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); /** * This calls glEGLImageTargetTexture2DOES to bind the image to the * texture in the specified texture target. */ void bindToTextureTarget(uint32_t texTarget); const sp& graphicBuffer() { return mGraphicBuffer; } const native_handle* graphicBufferHandle() { return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; } private: // Only allow instantiation using ref counting. friend class LightRefBase; virtual ~EglImage(); // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); // Disallow copying EglImage(const EglImage& rhs); void operator=(const EglImage& rhs); // mGraphicBuffer is the buffer that was used to create this image. sp mGraphicBuffer; // mEglImage is the EGLImage created from mGraphicBuffer. EGLImageKHR mEglImage; // mEGLDisplay is the EGLDisplay that was used to create mEglImage. EGLDisplay mEglDisplay; // mCropRect is the crop rectangle passed to EGL when mEglImage // was created. Rect mCropRect; }; /** * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command * stream to ensure that it is safe for future OpenGL ES commands to * access the current texture buffer. */ status_t doGLFenceWaitLocked(SurfaceTexture& st) const; /** * syncForReleaseLocked performs the synchronization needed to release the * current slot from an OpenGL ES context. If needed it will set the * current slot's fence to guard against a producer accessing the buffer * before the outstanding accesses have completed. */ status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); /** * returns a graphic buffer used when the texture image has been released */ static sp getDebugTexImageBuffer(); /** * The default consumer usage flags that EGLConsumer always sets on its * BufferQueue instance; these will be OR:d with any additional flags passed * from the EGLConsumer user. In particular, EGLConsumer will always * consume buffers as hardware textures. */ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; /** * mCurrentTextureImage is the EglImage/buffer of the current texture. It's * possible that this buffer is not associated with any buffer slot, so we * must track it separately in order to support the getCurrentBuffer method. */ sp mCurrentTextureImage; /** * EGLSlot contains the information and object references that * EGLConsumer maintains about a BufferQueue buffer slot. */ struct EglSlot { EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} /** * mEglImage is the EGLImage created from mGraphicBuffer. */ sp mEglImage; /** * mFence is the EGL sync object that must signal before the buffer * associated with this buffer slot may be dequeued. It is initialized * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based * on a compile-time option) set to a new sync object in updateTexImage. */ EGLSyncKHR mEglFence; }; /** * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently * associated. It is intialized to EGL_NO_DISPLAY and gets set to the * current display when updateTexImage is called for the first time and when * attachToContext is called. */ EGLDisplay mEglDisplay; /** * mEglContext is the OpenGL ES context with which this EGLConsumer is * currently associated. It is initialized to EGL_NO_CONTEXT and gets set * to the current GL context when updateTexImage is called for the first * time and when attachToContext is called. */ EGLContext mEglContext; /** * mEGLSlots stores the buffers that have been allocated by the BufferQueue * for each buffer slot. It is initialized to null pointers, and gets * filled in with the result of BufferQueue::acquire when the * client dequeues a buffer from a * slot that has not yet been used. The buffer allocated to a slot will also * be replaced if the requested buffer usage or geometry differs from that * of the buffer allocated to a slot. */ EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; /** * protects static initialization */ static Mutex sStaticInitLock; /** * mReleasedTexImageBuffer is a dummy buffer used when in single buffer * mode and releaseTexImage() has been called */ static sp sReleasedTexImageBuffer; sp mReleasedTexImage; }; } // namespace android