/* * Copyright (C) 2014 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 "DeferredLayerUpdater.h" #include "GlLayer.h" #include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, Layer::Api layerApi) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) , mLayer(nullptr) , mLayerApi(layerApi) , mCreateLayerFn(createLayerFn) { renderState.registerDeferredLayerUpdater(this); } DeferredLayerUpdater::~DeferredLayerUpdater() { SkSafeUnref(mColorFilter); setTransform(nullptr); mRenderState.unregisterDeferredLayerUpdater(this); destroyLayer(); } void DeferredLayerUpdater::destroyLayer() { if (!mLayer) { return; } if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { status_t err = mSurfaceTexture->detachFromContext(); mGLContextAttached = false; if (err != 0) { // TODO: Elevate to fatal exception ALOGE("Failed to detach SurfaceTexture from context %d", err); } } mLayer->postDecStrong(); mLayer = nullptr; } void DeferredLayerUpdater::setPaint(const SkPaint* paint) { mAlpha = PaintUtils::getAlphaDirect(paint); mMode = PaintUtils::getBlendModeDirect(paint); SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr; SkRefCnt_SafeAssign(mColorFilter, colorFilter); } void DeferredLayerUpdater::apply() { if (!mLayer) { mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { if (mLayer->getApi() == Layer::Api::Vulkan) { if (mUpdateTexImage) { mUpdateTexImage = false; doUpdateVkTexImage(); } } else { LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, "apply surfaceTexture with non GL backend %x, GL %x, VK %x", mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); if (!mGLContextAttached) { mGLContextAttached = true; mUpdateTexImage = true; mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId()); } if (mUpdateTexImage) { mUpdateTexImage = false; doUpdateTexImage(); } GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); static_cast<GlLayer*>(mLayer)->setRenderTarget(renderTarget); } if (mTransform) { mLayer->getTransform().load(*mTransform); setTransform(nullptr); } } } void DeferredLayerUpdater::doUpdateTexImage() { LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); if (mSurfaceTexture->updateTexImage() == NO_ERROR) { float transform[16]; int64_t frameNumber = mSurfaceTexture->getFrameNumber(); // If the GLConsumer queue is in synchronous mode, need to discard all // but latest frame, using the frame number to tell when we no longer // have newer frames to target. Since we can't tell which mode it is in, // do this unconditionally. int dropCounter = 0; while (mSurfaceTexture->updateTexImage() == NO_ERROR) { int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); if (newFrameNumber == frameNumber) break; frameNumber = newFrameNumber; dropCounter++; } bool forceFilter = false; sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer(); if (buffer != nullptr) { // force filtration if buffer size != layer size forceFilter = mWidth != static_cast<int>(buffer->getWidth()) || mHeight != static_cast<int>(buffer->getHeight()); } #if DEBUG_RENDERER if (dropCounter > 0) { RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); } #endif mSurfaceTexture->getTransformMatrix(transform); updateLayer(forceFilter, transform); } } void DeferredLayerUpdater::doUpdateVkTexImage() { LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); static const mat4 identityMatrix; updateLayer(false, identityMatrix.data); VkLayer* vkLayer = static_cast<VkLayer*>(mLayer); vkLayer->updateTexture(); } void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); mLayer->getTexTransform().load(textureTransform); } void DeferredLayerUpdater::detachSurfaceTexture() { if (mSurfaceTexture.get()) { destroyLayer(); mSurfaceTexture = nullptr; } } } /* namespace uirenderer */ } /* namespace android */