status_t GLConsumer::attachToContext(uint32_t tex) { ATRACE_CALL(); GLC_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("attachToContext: abandoned GLConsumer"); return NO_INIT; } if (mAttached) { GLC_LOGE("attachToContext: GLConsumer is already attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (dpy == EGL_NO_DISPLAY) { GLC_LOGE("attachToContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (ctx == EGL_NO_CONTEXT) { GLC_LOGE("attachToContext: invalid current EGLContext"); return INVALID_OPERATION; } // We need to bind the texture regardless of whether there's a current // buffer. glBindTexture(mTexTarget, GLuint(tex)); mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; if (mCurrentTextureImage != NULL) { // This may wait for a buffer a second time. This is likely required if // this is a different context, since otherwise the wait could be skipped // by bouncing through another context. For the same context the extra // wait is redundant. status_t err = bindTextureImageLocked(); if (err != NO_ERROR) { return err; } } return OK; }
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 SurfaceFlingerConsumer::bindTextureImage() { Mutex::Autolock lock(mMutex); return bindTextureImageLocked(); }
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) { ATRACE_CALL(); ALOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ALOGE("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); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // This variant of updateTexImage does not guarantee that the // texture is bound, so no need to call glBindTexture. err = NO_ERROR; } else { ALOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // We call the rejecter here, in case the caller has a reason to // not accept this buffer. This is used by SurfaceFlinger to // reject buffers which have the wrong size int buf = item.mBuf; if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, EGL_NO_SYNC_KHR); return NO_ERROR; } // Release the previous buffer. err = releaseAndUpdateLocked(item); if (err != NO_ERROR) { return err; } if (!SyncFeatures::getInstance().useNativeFenceSync()) { // Bind the new buffer to the GL texture. // // Older devices require the "implicit" synchronization provided // by glEGLImageTargetTexture2DOES, which this method calls. Newer // devices will either call this in Layer::onDraw, or (if it's not // a GL-composited layer) not at all. err = bindTextureImageLocked(); } return err; }
status_t GLConsumer::releaseTexImage() { ATRACE_CALL(); GLC_LOGV("releaseTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("releaseTexImage: GLConsumer is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = NO_ERROR; if (mAttached) { err = checkAndUpdateEglStateLocked(true); if (err != NO_ERROR) { return err; } } else { // if we're detached, no need to validate EGL's state -- we won't use it. } // Update the GLConsumer state. int buf = mCurrentTexture; if (buf != BufferQueue::INVALID_BUFFER_SLOT) { GLC_LOGV("releaseTexImage: (slot=%d, mAttached=%d)", buf, mAttached); if (mAttached) { // Do whatever sync ops we need to do before releasing the slot. err = syncForReleaseLocked(mEglDisplay); if (err != NO_ERROR) { GLC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); return err; } } else { // if we're detached, we just use the fence that was created in detachFromContext() // so... basically, nothing more to do here. } err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); if (err < NO_ERROR) { GLC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); return err; } if (mReleasedTexImage == NULL) { mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); } mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; mCurrentTextureImage = mReleasedTexImage; mCurrentCrop.makeInvalid(); mCurrentTransform = 0; mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mCurrentTimestamp = 0; mCurrentFence = Fence::NO_FENCE; if (mAttached) { // This binds a dummy buffer (mReleasedTexImage). status_t result = bindTextureImageLocked(); if (result != NO_ERROR) { return result; } } else { // detached, don't touch the texture (and we may not even have an // EGLDisplay here. } } return NO_ERROR; }