status_t SurfaceTexture::setBufferCount(int bufferCount) { ST_LOGV("setBufferCount: count=%d", bufferCount); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); return NO_INIT; } if (bufferCount > NUM_BUFFER_SLOTS) { ST_LOGE("setBufferCount: bufferCount larger than slots available"); return BAD_VALUE; } // Error out if the user has dequeued buffers for (int i=0 ; i<mBufferCount ; i++) { if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { ST_LOGE("setBufferCount: client owns some buffers"); return -EINVAL; } } const int minBufferSlots = mSynchronousMode ? MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; if (bufferCount == 0) { mClientBufferCount = 0; bufferCount = (mServerBufferCount >= minBufferSlots) ? mServerBufferCount : minBufferSlots; return setBufferCountServerLocked(bufferCount); } if (bufferCount < minBufferSlots) { ST_LOGE("setBufferCount: requested buffer count (%d) is less than " "minimum (%d)", bufferCount, minBufferSlots); return BAD_VALUE; } // here we're guaranteed that the client doesn't have dequeued buffers // and will release all of its buffer references. freeAllBuffersLocked(); mBufferCount = bufferCount; mClientBufferCount = bufferCount; mCurrentTexture = INVALID_BUFFER_SLOT; mQueue.clear(); mDequeueCondition.signal(); return OK; }
status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) { ST_LOGV("consumerConnect"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("consumerConnect: BufferQueue has been abandoned!"); return NO_INIT; } if (consumerListener == NULL) { ST_LOGE("consumerConnect: consumerListener may not be NULL"); return BAD_VALUE; } mConsumerListener = consumerListener; return NO_ERROR; }
status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { ATRACE_CALL(); ST_LOGV("requestBuffer: slot=%d", slot); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } if (slot < 0 || mBufferCount <= slot) { ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", mBufferCount, slot); return BAD_VALUE; } mSlots[slot].mRequestBufferCalled = true; *buf = mSlots[slot].mGraphicBuffer; return NO_ERROR; }
status_t GLConsumer::updateTexImage() { ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("updateTexImage: GLConsumer is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } BufferQueue::BufferItem item; // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item, 0); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. ST_LOGV("updateTexImage: no buffers were available"); glBindTexture(mTexTarget, mTexName); err = NO_ERROR; } else { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // Release the previous buffer. err = updateAndReleaseLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); return err; } // Bind the new buffer to the GL texture, and wait until it's ready. return bindTextureImageLocked(); }
status_t BufferQueue::disconnect(int api) { ATRACE_CALL(); ST_LOGV("disconnect: api=%d", api); int err = NO_ERROR; sp<ConsumerListener> listener; { // Scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { // it is not really an error to disconnect after the surface // has been abandoned, it should just be a no-op. return NO_ERROR; } switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi == api) { drainQueueAndFreeBuffersLocked(); mConnectedApi = NO_CONNECTED_API; mDequeueCondition.broadcast(); listener = mConsumerListener; } else { ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", mConnectedApi, api); err = -EINVAL; } break; default: ST_LOGE("disconnect: unknown API %d", api); err = -EINVAL; break; } } if (listener != NULL) { listener->onBuffersReleased(); } return err; }
status_t BufferQueue::acquireBuffer(BufferItem *buffer) { ATRACE_CALL(); Mutex::Autolock _l(mMutex); // Check that the consumer doesn't currently have the maximum number of // buffers acquired. We allow the max buffer count to be exceeded by one // buffer, so that the consumer can successfully set up the newly acquired // buffer before releasing the old one. int numAcquiredBuffers = 0; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) { numAcquiredBuffers++; } } if (numAcquiredBuffers >= mMaxAcquiredBufferCount+1) { ST_LOGE("acquireBuffer: max acquired buffer count reached: %d (max=%d)", numAcquiredBuffers, mMaxAcquiredBufferCount); return INVALID_OPERATION; } // check if queue is empty // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. if (!mQueue.empty()) { Fifo::iterator front(mQueue.begin()); int buf = *front; ATRACE_BUFFER_INDEX(buf); if (mSlots[buf].mAcquireCalled) { buffer->mGraphicBuffer = NULL; } else { buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; } buffer->mCrop = mSlots[buf].mCrop; buffer->mTransform = mSlots[buf].mTransform; buffer->mScalingMode = mSlots[buf].mScalingMode; buffer->mFrameNumber = mSlots[buf].mFrameNumber; buffer->mTimestamp = mSlots[buf].mTimestamp; buffer->mBuf = buf; buffer->mFence = mSlots[buf].mFence; mSlots[buf].mAcquireCalled = true; mSlots[buf].mNeedsCleanupOnRelease = false; mSlots[buf].mBufferState = BufferSlot::ACQUIRED; mSlots[buf].mFence = Fence::NO_FENCE; mQueue.erase(front); mDequeueCondition.broadcast(); ATRACE_INT(mConsumerName.string(), mQueue.size()); } else { return NO_BUFFER_AVAILABLE; } return NO_ERROR; }
status_t SurfaceTexture::setTransform(uint32_t transform) { ST_LOGV("setTransform: xform=%#x", transform); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("setTransform: SurfaceTexture has been abandoned!"); return NO_INIT; } mNextTransform = transform; return OK; }
void SurfaceTexture::setReleaseFence(int fenceFd) { sp<Fence> fence(new Fence(fenceFd)); if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) return; status_t err = addReleaseFence(mCurrentTexture, fence); if (err != OK) { ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); } }
void GLConsumer::setReleaseFence(const sp<Fence>& fence) { if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t err = addReleaseFence(mCurrentTexture, fence); if (err != OK) { ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); } } }
status_t GLConsumer::checkAndUpdateEglStateLocked() { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; return NO_ERROR; }
status_t SurfaceTexture::setCrop(const Rect& crop) { ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, crop.bottom); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("setCrop: SurfaceTexture has been abandoned!"); return NO_INIT; } mNextCrop = crop; return OK; }
status_t BufferQueue::connect(int api, QueueBufferOutput* output) { ATRACE_CALL(); ST_LOGV("connect: api=%d", api); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("connect: BufferQueue has been abandoned!"); return NO_INIT; } if (mConsumerListener == NULL) { ST_LOGE("connect: BufferQueue has no consumer!"); return NO_INIT; } int err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi != NO_CONNECTED_API) { ST_LOGE("connect: already connected (cur=%d, req=%d)", mConnectedApi, api); err = -EINVAL; } else { mConnectedApi = api; output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); } break; default: err = -EINVAL; break; } mBufferHasBeenQueued = false; return err; }
void SurfaceTexture::cancelBuffer(int buf) { ST_LOGV("cancelBuffer: slot=%d", buf); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!"); return; } if (buf < 0 || buf >= mBufferCount) { ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); return; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return; } mSlots[buf].mBufferState = BufferSlot::FREE; mSlots[buf].mFrameNumber = 0; mDequeueCondition.signal(); }
status_t SurfaceTexture::doGLFenceWaitLocked() const { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { ST_LOGE("doGLFenceWait: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { ST_LOGE("doGLFenceWait: invalid current EGLContext"); return INVALID_OPERATION; } if (mCurrentFence != NULL) { if (useWaitSync) { // Create an EGLSyncKHR from the current fence. int fenceFd = mCurrentFence->dup(); if (fenceFd == -1) { ST_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); return -errno; } EGLint attribs[] = { EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE }; EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (sync == EGL_NO_SYNC_KHR) { close(fenceFd); ST_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); return UNKNOWN_ERROR; } // XXX: The spec draft is inconsistent as to whether this should // return an EGLint or void. Ignore the return value for now, as // it's not strictly needed. eglWaitSyncANDROID(dpy, sync, 0); EGLint eglErr = eglGetError(); eglDestroySyncKHR(dpy, sync); if (eglErr != EGL_SUCCESS) { ST_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); return UNKNOWN_ERROR; } } else { status_t err = mCurrentFence->waitForever(1000, "SurfaceTexture::doGLFenceWaitLocked"); if (err != NO_ERROR) { ST_LOGE("doGLFenceWait: error waiting for fence: %d", err); return err; } } } return NO_ERROR; }
status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) { ST_LOGV("consumerConnect"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("consumerConnect: BufferQueue has been abandoned!"); return NO_INIT; } mConsumerListener = consumerListener; return OK; }
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) : mCurrentTransform(0), mCurrentTimestamp(0), mFilteringEnabled(true), mTexName(tex), #ifdef USE_FENCE_SYNC mUseFenceSync(useFenceSync), #else mUseFenceSync(false), #endif mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), mAbandoned(false), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); ST_LOGV("SurfaceTexture"); if (bufferQueue == 0) { ST_LOGV("Creating a new BufferQueue"); mBufferQueue = new BufferQueue(allowSynchronousMode); } else { mBufferQueue = bufferQueue; } memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); // Note that we can't create an sp<...>(this) in a ctor that will not keep a // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. wp<BufferQueue::ConsumerListener> listener; sp<BufferQueue::ConsumerListener> proxy; listener = static_cast<BufferQueue::ConsumerListener*>(this); proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mBufferQueue->consumerConnect(proxy); if (err != NO_ERROR) { ST_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { mBufferQueue->setConsumerName(mName); mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } }
status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) { ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); if (!w || !h) { ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)", w, h); return BAD_VALUE; } Mutex::Autolock lock(mMutex); mDefaultWidth = w; mDefaultHeight = h; return OK; }
status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > MAX_MAX_ACQUIRED_BUFFERS) { ST_LOGE("setMaxAcquiredBufferCount: invalid count specified: %d", maxAcquiredBuffers); return BAD_VALUE; } if (mConnectedApi != NO_CONNECTED_API) { return INVALID_OPERATION; } mMaxAcquiredBufferCount = maxAcquiredBuffers; return NO_ERROR; }
status_t GLConsumer::bindTextureImageLocked() { if (mEglDisplay == EGL_NO_DISPLAY) { ALOGE("bindTextureImage: invalid display"); return INVALID_OPERATION; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("bindTextureImage: clearing GL error: %#04x", error); } glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { if (mCurrentTextureBuf == NULL) { ST_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } status_t err = bindUnslottedBufferLocked(mEglDisplay); if (err != NO_ERROR) { return err; } } else { EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage; glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("bindTextureImage: error binding external texture image %p" ": %#04x", image, error); return UNKNOWN_ERROR; } } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); }
status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to wait // on that before replacing it with another fence to ensure that all // outstanding buffer accesses have completed before the producer // accesses it. EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); if (result == EGL_FALSE) { ST_LOGE("syncForReleaseLocked: error waiting for previous " "fence: %#x", eglGetError()); return UNKNOWN_ERROR; } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ST_LOGE("syncForReleaseLocked: timeout waiting for previous " "fence"); return TIMED_OUT; } eglDestroySyncKHR(dpy, fence); } // Create a fence for the outstanding accesses in the current OpenGL ES // context. fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { ST_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); mEGLSlots[mCurrentTexture].mFence = fence; } return OK; }
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, }; EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { EGLint error = eglGetError(); ST_LOGE("error creating EGLImage: %#x", error); } return image; }
status_t SurfaceTexture::disconnect(int api) { ST_LOGV("disconnect: api=%d", api); Mutex::Autolock lock(mMutex); if (mAbandoned) { // it is not really an error to disconnect after the surface // has been abandoned, it should just be a no-op. return NO_ERROR; } int err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi == api) { drainQueueAndFreeBuffersLocked(); mConnectedApi = NO_CONNECTED_API; mNextCrop.makeInvalid(); mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mNextTransform = 0; mDequeueCondition.signal(); } else { ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", mConnectedApi, api); err = -EINVAL; } break; default: ST_LOGE("disconnect: unknown API %d", api); err = -EINVAL; break; } return err; }
status_t BufferQueue::consumerDisconnect() { ST_LOGV("consumerDisconnect"); Mutex::Autolock lock(mMutex); if (mConsumerListener == NULL) { ST_LOGE("consumerDisconnect: No consumer is connected!"); return -EINVAL; } mAbandoned = true; mConsumerListener = NULL; mQueue.clear(); freeAllBuffersLocked(); mDequeueCondition.broadcast(); return OK; }
status_t SurfaceTexture::setScalingMode(int mode) { ST_LOGV("setScalingMode: mode=%d", mode); switch (mode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: break; default: ST_LOGE("unknown scaling mode: %d", mode); return BAD_VALUE; } Mutex::Autolock lock(mMutex); mNextScalingMode = mode; return OK; }
void SurfaceTexture::setFilteringEnabled(bool enabled) { Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); return; } bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; if (needsRecompute && mCurrentTextureBuf==NULL) { ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL"); } if (needsRecompute && mCurrentTextureBuf != NULL) { computeCurrentTransformMatrixLocked(); } }
status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) { ST_LOGV("getReleasedBuffers"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("getReleasedBuffers: BufferQueue has been abandoned!"); return NO_INIT; } uint32_t mask = 0; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (!mSlots[i].mAcquireCalled) { mask |= 1 << i; } } *slotMask = mask; ST_LOGV("getReleasedBuffers: returning mask %#x", mask); return NO_ERROR; }
EGLImageKHR GLConsumer::createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, EGL_IMAGE_CROP_TOP_ANDROID, crop.top, EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, EGL_NONE, }; if (!crop.isValid()) { // No crop rect to set, so terminate the attrib array before the crop. attrs[2] = EGL_NONE; } else if (!isEglImageCroppable(crop)) { // The crop rect is not at the origin, so we can't set the crop on the // EGLImage because that's not allowed by the EGL_ANDROID_image_crop // extension. In the future we can add a layered extension that // removes this restriction if there is hardware that can support it. attrs[2] = EGL_NONE; } EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { EGLint error = eglGetError(); ST_LOGE("error creating EGLImage: %#x", error); } #ifndef MTK_DEFAULT_AOSP else { // add log for eglImage created ST_LOGI("[%s]", __func__); ALOGD(" GraphicBuffer: gb=%p handle=%p fmt=%d", graphicBuffer.get(), graphicBuffer->handle, graphicBuffer->format); ALOGD(" EGLImage: dpy=%p, img=%p", mEglDisplay, image); } #endif return image; }
status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (useNativeFenceSync) { EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); if (sync == EGL_NO_SYNC_KHR) { ST_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); eglDestroySyncKHR(dpy, sync); if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { ST_LOGE("syncForReleaseLocked: error dup'ing native fence " "fd: %#x", eglGetError()); return UNKNOWN_ERROR; } sp<Fence> fence(new Fence(fenceFd)); status_t err = addReleaseFenceLocked(mCurrentTexture, fence); if (err != OK) { ST_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", strerror(-err), err); return err; } } else if (mUseFenceSync) { EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to // wait on that before replacing it with another fence to // ensure that all outstanding buffer accesses have completed // before the producer accesses it. EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); if (result == EGL_FALSE) { ST_LOGE("syncForReleaseLocked: error waiting for previous " "fence: %#x", eglGetError()); return UNKNOWN_ERROR; } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ST_LOGE("syncForReleaseLocked: timeout waiting for previous " "fence"); return TIMED_OUT; } eglDestroySyncKHR(dpy, fence); } // Create a fence for the outstanding accesses in the current // OpenGL ES context. fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { ST_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; } } return OK; }
status_t SurfaceTexture::attachToContext(GLuint tex) { ATRACE_CALL(); ST_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("attachToContext: abandoned SurfaceTexture"); return NO_INIT; } if (mAttached) { ST_LOGE("attachToContext: SurfaceTexture is already attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (dpy == EGL_NO_DISPLAY) { ST_LOGE("attachToContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (ctx == EGL_NO_CONTEXT) { ST_LOGE("attachToContext: invalid current EGLContext"); return INVALID_OPERATION; } // We need to bind the texture regardless of whether there's a current // buffer. glBindTexture(mTexTarget, tex); if (mCurrentTextureBuf != NULL) { // The EGLImageKHR that was associated with the slot was destroyed when // the SurfaceTexture was detached from the old context, so we need to // recreate it here. EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; } // Attach the current buffer to the GL texture. glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); GLint error; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("attachToContext: error binding external texture image %p " "(slot %d): %#04x", image, mCurrentTexture, error); err = UNKNOWN_ERROR; } // We destroy the EGLImageKHR here because the current buffer may no // longer be associated with one of the buffer slots, so we have // nowhere to to store it. If the buffer is still associated with a // slot then another EGLImageKHR will be created next time that buffer // gets acquired in updateTexImage. eglDestroyImageKHR(dpy, image); if (err != OK) { return err; } } mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; return OK; }
status_t SurfaceTexture::detachFromContext() { ATRACE_CALL(); ST_LOGV("detachFromContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("detachFromContext: abandoned SurfaceTexture"); return NO_INIT; } if (!mAttached) { ST_LOGE("detachFromContext: SurfaceTexture is not attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { ST_LOGE("detachFromContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { ST_LOGE("detachFromContext: invalid current EGLContext"); return INVALID_OPERATION; } if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { status_t err = syncForReleaseLocked(dpy); if (err != OK) { return err; } glDeleteTextures(1, &mTexName); } // Because we're giving up the EGLDisplay we need to free all the EGLImages // that are associated with it. They'll be recreated when the // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a // new EGLDisplay). for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { EGLImageKHR img = mEglSlots[i].mEglImage; if (img != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mEglDisplay, img); mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR; } } #ifdef STE_HARDWARE if (mBlitEngine) { copybit_close(mBlitEngine); } #endif mEglDisplay = EGL_NO_DISPLAY; mEglContext = EGL_NO_CONTEXT; mAttached = false; return OK; }