diff options
Diffstat (limited to 'services/surfaceflinger/BufferStateLayer.cpp')
| -rw-r--r-- | services/surfaceflinger/BufferStateLayer.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp new file mode 100644 index 0000000000..2e411f1ab8 --- /dev/null +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2017 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. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "BufferStateLayer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "BufferStateLayer.h" +#include "RenderEngine/Image.h" +#include "clz.h" + +#include <private/gui/SyncFeatures.h> + +namespace android { + +static const std::array<float, 16> IDENTITY_MATRIX{1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1}; + +BufferStateLayer::BufferStateLayer(SurfaceFlinger* flinger, const sp<Client>& client, + const String8& name, uint32_t w, uint32_t h, uint32_t flags) + : BufferLayer(flinger, client, name, w, h, flags), + mSidebandStreamChanged(false), + mFrameNumber(0) { + mTransformMatrix = IDENTITY_MATRIX; +} + +// ----------------------------------------------------------------------- +// Interface implementation for Layer +// ----------------------------------------------------------------------- +void BufferStateLayer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) { + // TODO(marissaw): send the release fence back to buffer owner + return; +} + +void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const { + // TODO(marissaw): send the transform hint to buffer owner + return; +} + +void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { + // TODO(marissaw): use this to signal the buffer owner + return; +} + +bool BufferStateLayer::shouldPresentNow(const DispSync& /*dispSync*/) const { + if (getSidebandStreamChanged() || getAutoRefresh()) { + return true; + } + + return hasDrawingBuffer(); +} + +bool BufferStateLayer::getTransformToDisplayInverse() const { + return mCurrentState.transformToDisplayInverse; +} + +void BufferStateLayer::pushPendingState() { + if (!mCurrentState.modified) { + return; + } + mPendingStates.push_back(mCurrentState); + ATRACE_INT(mTransactionName.string(), mPendingStates.size()); +} + +bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) { + const bool stateUpdateAvailable = !mPendingStates.empty(); + while (!mPendingStates.empty()) { + popPendingState(stateToCommit); + } + mCurrentState.modified = false; + return stateUpdateAvailable; +} + +Rect BufferStateLayer::getCrop(const Layer::State& s) const { + return (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? GLConsumer::scaleDownCrop(s.crop, s.active.w, s.active.h) + : s.crop; +} + +bool BufferStateLayer::setTransform(uint32_t transform) { + if (mCurrentState.transform == transform) return false; + mCurrentState.sequence++; + mCurrentState.transform = transform; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) { + if (mCurrentState.transformToDisplayInverse == transformToDisplayInverse) return false; + mCurrentState.sequence++; + mCurrentState.transformToDisplayInverse = transformToDisplayInverse; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setCrop(const Rect& crop) { + if (mCurrentState.crop == crop) return false; + mCurrentState.sequence++; + mCurrentState.crop = crop; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setBuffer(sp<GraphicBuffer> buffer) { + mCurrentState.sequence++; + mCurrentState.buffer = buffer; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) { + mCurrentState.acquireFence = fence; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) { + if (mCurrentState.dataspace == dataspace) return false; + mCurrentState.sequence++; + mCurrentState.dataspace = dataspace; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) { + if (mCurrentState.hdrMetadata == hdrMetadata) return false; + mCurrentState.sequence++; + mCurrentState.hdrMetadata = hdrMetadata; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) { + mCurrentState.sequence++; + mCurrentState.surfaceDamageRegion = surfaceDamage; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setApi(int32_t api) { + if (mCurrentState.api == api) return false; + mCurrentState.sequence++; + mCurrentState.api = api; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) { + if (mCurrentState.sidebandStream == sidebandStream) return false; + mCurrentState.sequence++; + mCurrentState.sidebandStream = sidebandStream; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + + if (!mSidebandStreamChanged.exchange(true)) { + // mSidebandStreamChanged was false + mFlinger->signalLayerUpdate(); + } + return true; +} + +bool BufferStateLayer::setSize(uint32_t w, uint32_t h) { + if (mCurrentState.active.w == w && mCurrentState.active.h == h) return false; + mCurrentState.active.w = w; + mCurrentState.active.h = h; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setPosition(float x, float y, bool /*immediate*/) { + if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y) + return false; + + mCurrentState.active.transform.set(x, y); + + mCurrentState.sequence++; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) { + mCurrentState.transparentRegionHint = transparent; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, + bool allowNonRectPreservingTransforms) { + Transform t; + t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + + if (!allowNonRectPreservingTransforms && !t.preserveRects()) { + ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER ignored"); + return false; + } + + mCurrentState.sequence++; + mCurrentState.active.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} +// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayer +// ----------------------------------------------------------------------- +bool BufferStateLayer::fenceHasSignaled() const { + if (latchUnsignaledBuffers()) { + return true; + } + + return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; +} + +nsecs_t BufferStateLayer::getDesiredPresentTime() { + // TODO(marissaw): support an equivalent to desiredPresentTime for timestats metrics + return 0; +} + +std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const { + return std::make_shared<FenceTime>(getDrawingState().acquireFence); +} + +void BufferStateLayer::getDrawingTransformMatrix(float *matrix) { + std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix), matrix); +} + +uint32_t BufferStateLayer::getDrawingTransform() const { + return getDrawingState().transform; +} + +ui::Dataspace BufferStateLayer::getDrawingDataSpace() const { + return getDrawingState().dataspace; +} + +Rect BufferStateLayer::getDrawingCrop() const { + return Rect::INVALID_RECT; +} + +uint32_t BufferStateLayer::getDrawingScalingMode() const { + return NATIVE_WINDOW_SCALING_MODE_FREEZE; +} + +Region BufferStateLayer::getDrawingSurfaceDamage() const { + return getDrawingState().surfaceDamageRegion; +} + +const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const { + return getDrawingState().hdrMetadata; +} + +int BufferStateLayer::getDrawingApi() const { + return getDrawingState().api; +} + +PixelFormat BufferStateLayer::getPixelFormat() const { + return mActiveBuffer->format; +} + +uint64_t BufferStateLayer::getFrameNumber() const { + return mFrameNumber; +} + +bool BufferStateLayer::getAutoRefresh() const { + // TODO(marissaw): support shared buffer mode + return false; +} + +bool BufferStateLayer::getSidebandStreamChanged() const { + return mSidebandStreamChanged.load(); +} + +std::optional<Region> BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { + if (mSidebandStreamChanged.exchange(false)) { + const State& s(getDrawingState()); + // mSidebandStreamChanged was true + // replicated in LayerBE until FE/BE is ready to be synchronized + getBE().compositionInfo.hwc.sidebandStream = s.sidebandStream; + if (getBE().compositionInfo.hwc.sidebandStream != nullptr) { + setTransactionFlags(eTransactionNeeded); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + recomputeVisibleRegions = true; + + return getTransform().transform(Region(Rect(s.active.w, s.active.h))); + } + return {}; +} + +bool BufferStateLayer::hasDrawingBuffer() const { + return getDrawingState().buffer != nullptr; +} + +void BufferStateLayer::setFilteringEnabled(bool enabled) { + GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop, + mCurrentTransform, enabled); +} + +status_t BufferStateLayer::bindTextureImage() const { + const State& s(getDrawingState()); + auto& engine(mFlinger->getRenderEngine()); + + if (!engine.isCurrent()) { + ALOGE("RenderEngine is not current"); + return INVALID_OPERATION; + } + + engine.checkErrors(); + + if (!mTextureImage) { + ALOGE("no currently-bound texture"); + engine.bindExternalTextureImage(mTextureName, *engine.createImage()); + return NO_INIT; + } + + bool created = + mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(), + s.buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + if (!created) { + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + s.buffer->getWidth(), s.buffer->getHeight(), s.buffer->getStride(), + s.buffer->getUsage(), s.buffer->getPixelFormat()); + engine.bindExternalTextureImage(mTextureName, *engine.createImage()); + return NO_INIT; + } + + engine.bindExternalTextureImage(mTextureName, *mTextureImage); + + // Wait for the new buffer to be ready. + if (s.acquireFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync()) { + base::unique_fd fenceFd(s.acquireFence->dup()); + if (fenceFd == -1) { + ALOGE("error dup'ing fence fd: %d", errno); + return -errno; + } + if (!engine.waitFence(std::move(fenceFd))) { + ALOGE("failed to wait on fence fd"); + return UNKNOWN_ERROR; + } + } else { + status_t err = s.acquireFence->waitForever("BufferStateLayer::bindTextureImage"); + if (err != NO_ERROR) { + ALOGE("error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) { + const State& s(getDrawingState()); + + if (!s.buffer) { + return NO_ERROR; + } + + auto& engine(mFlinger->getRenderEngine()); + if (!engine.isCurrent()) { + ALOGE("RenderEngine is not current"); + return INVALID_OPERATION; + } + engine.checkErrors(); + + // TODO(marissaw): once buffers are cached, don't create a new image everytime + mTextureImage = engine.createImage(); + + // Reject if the layer is invalid + uint32_t bufferWidth = s.buffer->width; + uint32_t bufferHeight = s.buffer->height; + + if (s.transform & Transform::ROT_90) { + swap(bufferWidth, bufferHeight); + } + + if (s.transformToDisplayInverse) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + if (invTransform & Transform::ROT_90) { + swap(bufferWidth, bufferHeight); + } + } + + if (mOverrideScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE && + (s.active.w != bufferWidth || s.active.h != bufferHeight)) { + ALOGE("[%s] rejecting buffer: " + "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}", + mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h); + mTimeStats.removeTimeRecord(getName().c_str(), getFrameNumber()); + return BAD_VALUE; + } + + // Handle sync fences + if (SyncFeatures::getInstance().useNativeFenceSync()) { + base::unique_fd fenceFd = engine.flush(); + if (fenceFd == -1) { + ALOGE("failed to flush RenderEngine"); + mTimeStats.clearLayerRecord(getName().c_str()); + return UNKNOWN_ERROR; + } + + sp<Fence> fence(new Fence(std::move(fenceFd))); + + // Check status of fences first because merging is expensive. + // Merging an invalid fence with any other fence results in an + // invalid fence. + auto currentStatus = s.acquireFence->getStatus(); + if (currentStatus == Fence::Status::Invalid) { + ALOGE("Existing fence has invalid state"); + mTimeStats.clearLayerRecord(getName().c_str()); + return BAD_VALUE; + } + + auto incomingStatus = fence->getStatus(); + if (incomingStatus == Fence::Status::Invalid) { + ALOGE("New fence has invalid state"); + mDrawingState.acquireFence = fence; + mTimeStats.clearLayerRecord(getName().c_str()); + return BAD_VALUE; + } + + // If both fences are signaled or both are unsignaled, we need to merge + // them to get an accurate timestamp. + if (currentStatus == incomingStatus) { + char fenceName[32] = {}; + snprintf(fenceName, 32, "%.28s:%d", mName.string(), mFrameNumber); + sp<Fence> mergedFence = Fence::merge(fenceName, mDrawingState.acquireFence, fence); + if (!mergedFence.get()) { + ALOGE("failed to merge release fences"); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mDrawingState.acquireFence = fence; + mTimeStats.clearLayerRecord(getName().c_str()); + return BAD_VALUE; + } + mDrawingState.acquireFence = mergedFence; + } else if (incomingStatus == Fence::Status::Unsignaled) { + // If one fence has signaled and the other hasn't, the unsignaled + // fence will approximately correspond with the correct timestamp. + // There's a small race if both fences signal at about the same time + // and their statuses are retrieved with unfortunate timing. However, + // by this point, they will have both signaled and only the timestamp + // will be slightly off; any dependencies after this point will + // already have been met. + mDrawingState.acquireFence = fence; + } + } else { + // 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. + status_t err = bindTextureImage(); + if (err != NO_ERROR) { + mTimeStats.clearLayerRecord(getName().c_str()); + return BAD_VALUE; + } + } + + // TODO(marissaw): properly support mTimeStats + const std::string layerName(getName().c_str()); + mTimeStats.setPostTime(getName().c_str(), getFrameNumber(), latchTime); + mTimeStats.setAcquireFence(layerName, getFrameNumber(), getCurrentFenceTime()); + mTimeStats.setLatchTime(layerName, getFrameNumber(), latchTime); + + return NO_ERROR; +} + +status_t BufferStateLayer::updateActiveBuffer() { + const State& s(getDrawingState()); + + if (s.buffer == nullptr) { + return BAD_VALUE; + } + + mActiveBuffer = s.buffer; + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.mBufferSlot = 0; + + return NO_ERROR; +} + +status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) { + // TODO(marissaw): support frame history events + mCurrentFrameNumber = mFrameNumber; + return NO_ERROR; +} + +void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) { + const auto displayId = display->getId(); + auto& hwcInfo = getBE().mHwcLayers[displayId]; + auto& hwcLayer = hwcInfo.layer; + + const State& s(getDrawingState()); + + // TODO(marissaw): support more than one slot + uint32_t hwcSlot = 0; + + auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), + s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error)); + } + + mFrameNumber++; +} + +void BufferStateLayer::onFirstRef() { + if (const auto display = mFlinger->getDefaultDisplayDevice()) { + updateTransformHint(display); + } +} + +} // namespace android |