/* * Copyright 2022 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 "SurfaceFlinger" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #include #include "LayerFE.h" #include "SurfaceFlinger.h" #include "common/FlagManager.h" #include "ui/FenceResult.h" #include "ui/LayerStack.h" namespace android { namespace { constexpr float defaultMaxLuminance = 1000.0; constexpr mat4 inverseOrientation(uint32_t transform) { const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); mat4 tr; if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { tr = tr * rot90; } if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { tr = tr * flipH; } if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { tr = tr * flipV; } return inverse(tr); } FloatRect reduce(const FloatRect& win, const Region& exclude) { if (CC_LIKELY(exclude.isEmpty())) { return win; } // Convert through Rect (by rounding) for lack of FloatRegion return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect(); } // Computes the transform matrix using the setFilteringEnabled to determine whether the // transform matrix should be computed for use with bilinear filtering. void getDrawingTransformMatrix(const std::shared_ptr& buffer, Rect bufferCrop, uint32_t bufferTransform, bool filteringEnabled, float outMatrix[16]) { if (!buffer) { ALOGE("Buffer should not be null!"); return; } GLConsumer::computeTransformMatrix(outMatrix, static_cast(buffer->getWidth()), static_cast(buffer->getHeight()), buffer->getPixelFormat(), bufferCrop, bufferTransform, filteringEnabled); } } // namespace LayerFE::LayerFE(const std::string& name) : mName(name) {} LayerFE::~LayerFE() { // Ensures that no promise is left unfulfilled before the LayerFE is destroyed. // An unfulfilled promise could occur when a screenshot is attempted, but the // render area is invalid and there is no memory for the capture result. if (FlagManager::getInstance().ce_fence_promise() && mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) { setReleaseFence(Fence::NO_FENCE); } } const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const { return mSnapshot.get(); } bool LayerFE::onPreComposition(bool) { return mSnapshot->hasReadyFrame; } std::optional LayerFE::prepareClientComposition( compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { std::optional layerSettings = prepareClientCompositionInternal(targetSettings); // Nothing to render. if (!layerSettings) { return {}; } // HWC requests to clear this layer. if (targetSettings.clearContent) { prepareClearClientComposition(*layerSettings, false /* blackout */); return layerSettings; } // set the shadow for the layer if needed prepareShadowClientComposition(*layerSettings, targetSettings.viewport); return layerSettings; } std::optional LayerFE::prepareClientCompositionInternal( compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { ATRACE_CALL(); compositionengine::LayerFE::LayerSettings layerSettings; layerSettings.geometry.boundaries = reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint); layerSettings.geometry.positionTransform = mSnapshot->geomLayerTransform.asMatrix4(); // skip drawing content if the targetSettings indicate the content will be occluded const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent; layerSettings.skipContentDraw = !drawContent; if (!mSnapshot->colorTransformIsIdentity) { layerSettings.colorTransform = mSnapshot->colorTransform; } const auto& roundedCornerState = mSnapshot->roundedCorner; layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius; layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect; layerSettings.alpha = mSnapshot->alpha; layerSettings.sourceDataspace = mSnapshot->dataspace; // Override the dataspace transfer from 170M to sRGB if the device configuration requests this. // We do this here instead of in buffer info so that dumpsys can still report layers that are // using the 170M transfer. if (targetSettings.treat170mAsSrgb && (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_SMPTE_170M) { layerSettings.sourceDataspace = static_cast( (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) | (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) | HAL_DATASPACE_TRANSFER_SRGB); } layerSettings.whitePointNits = targetSettings.whitePointNits; switch (targetSettings.blurSetting) { case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled: layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; layerSettings.blurRegions = mSnapshot->blurRegions; layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4(); break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly: layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly: layerSettings.blurRegions = mSnapshot->blurRegions; layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4(); break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled: default: break; } layerSettings.stretchEffect = mSnapshot->stretchEffect; // Record the name of the layer for debugging further down the stack. layerSettings.name = mSnapshot->name; if (hasEffect() && !hasBufferOrSidebandStream()) { prepareEffectsClientComposition(layerSettings, targetSettings); return layerSettings; } prepareBufferStateClientComposition(layerSettings, targetSettings); return layerSettings; } void LayerFE::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const { layerSettings.source.buffer.buffer = nullptr; layerSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f); layerSettings.disableBlending = true; layerSettings.bufferId = 0; layerSettings.frameNumber = 0; // If layer is blacked out, force alpha to 1 so that we draw a black color layer. layerSettings.alpha = blackout ? 1.0f : 0.0f; layerSettings.name = mSnapshot->name; } void LayerFE::prepareEffectsClientComposition( compositionengine::LayerFE::LayerSettings& layerSettings, compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { // If fill bounds are occluded or the fill color is invalid skip the fill settings. if (targetSettings.realContentIsVisible && fillsColor()) { // Set color for color fill settings. layerSettings.source.solidColor = mSnapshot->color.rgb; } else if (hasBlur() || drawShadows()) { layerSettings.skipContentDraw = true; } } void LayerFE::prepareBufferStateClientComposition( compositionengine::LayerFE::LayerSettings& layerSettings, compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { ATRACE_CALL(); if (CC_UNLIKELY(!mSnapshot->externalTexture)) { // If there is no buffer for the layer or we have sidebandstream where there is no // activeBuffer, then we need to return LayerSettings. return; } bool blackOutLayer; if (FlagManager::getInstance().display_protected()) { blackOutLayer = (mSnapshot->hasProtectedContent && !targetSettings.isProtected) || (mSnapshot->isSecure && !targetSettings.isSecure); } else { blackOutLayer = (mSnapshot->hasProtectedContent && !targetSettings.isProtected) || ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) && !targetSettings.isSecure); } const bool bufferCanBeUsedAsHwTexture = mSnapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE; if (blackOutLayer || !bufferCanBeUsedAsHwTexture) { ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable", mSnapshot->name.c_str()); prepareClearClientComposition(layerSettings, true /* blackout */); return; } layerSettings.source.buffer.buffer = mSnapshot->externalTexture; layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque; layerSettings.source.buffer.fence = mSnapshot->acquireFence; layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha; bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086; bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3; float maxLuminance = 0.f; if (hasSmpte2086 && hasCta861_3) { maxLuminance = std::min(mSnapshot->hdrMetadata.smpte2086.maxLuminance, mSnapshot->hdrMetadata.cta8613.maxContentLightLevel); } else if (hasSmpte2086) { maxLuminance = mSnapshot->hdrMetadata.smpte2086.maxLuminance; } else if (hasCta861_3) { maxLuminance = mSnapshot->hdrMetadata.cta8613.maxContentLightLevel; } else { switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: case HAL_DATASPACE_TRANSFER_HLG: // Behavior-match previous releases for HDR content maxLuminance = defaultMaxLuminance; break; } } layerSettings.source.buffer.maxLuminanceNits = maxLuminance; layerSettings.frameNumber = mSnapshot->frameNumber; layerSettings.bufferId = mSnapshot->externalTexture->getId(); const bool useFiltering = targetSettings.needsFiltering || mSnapshot->geomLayerTransform.needsBilinearFiltering(); // Query the texture matrix given our current filtering mode. float textureMatrix[16]; getDrawingTransformMatrix(layerSettings.source.buffer.buffer, mSnapshot->geomContentCrop, mSnapshot->geomBufferTransform, useFiltering, textureMatrix); if (mSnapshot->geomBufferUsesDisplayInverseTransform) { /* * the code below applies the primary display's inverse transform to * the texture transform */ uint32_t transform = SurfaceFlinger::getActiveDisplayRotationFlags(); mat4 tr = inverseOrientation(transform); /** * TODO(b/36727915): This is basically a hack. * * Ensure that regardless of the parent transformation, * this buffer is always transformed from native display * orientation to display orientation. For example, in the case * of a camera where the buffer remains in native orientation, * we want the pixels to always be upright. */ const auto parentTransform = mSnapshot->parentTransform; tr = tr * inverseOrientation(parentTransform.getOrientation()); // and finally apply it to the original texture matrix const mat4 texTransform(mat4(static_cast(textureMatrix)) * tr); memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix)); } const Rect win{layerSettings.geometry.boundaries}; float bufferWidth = static_cast(mSnapshot->bufferSize.getWidth()); float bufferHeight = static_cast(mSnapshot->bufferSize.getHeight()); // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has // been set and there is no parent layer bounds. In that case, the scale is meaningless so // ignore them. if (!mSnapshot->bufferSize.isValid()) { bufferWidth = float(win.right) - float(win.left); bufferHeight = float(win.bottom) - float(win.top); } const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight; const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth; const float translateY = float(win.top) / bufferHeight; const float translateX = float(win.left) / bufferWidth; // Flip y-coordinates because GLConsumer expects OpenGL convention. mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) * mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) * mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) * mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f)); layerSettings.source.buffer.useTextureFiltering = useFiltering; layerSettings.source.buffer.textureTransform = mat4(static_cast(textureMatrix)) * tr; return; } void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster, const Rect& layerStackRect) const { ShadowSettings state = mSnapshot->shadowSettings; if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) { return; } // Shift the spot light x-position to the middle of the display and then // offset it by casting layer's screen pos. state.lightPos.x = (static_cast(layerStackRect.width()) / 2.f) - mSnapshot->transformedBounds.left; state.lightPos.y -= mSnapshot->transformedBounds.top; caster.shadow = state; } void LayerFE::onLayerDisplayed(ftl::SharedFuture futureFenceResult, ui::LayerStack layerStack) { mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack); } CompositionResult&& LayerFE::stealCompositionResult() { return std::move(mCompositionResult); } const char* LayerFE::getDebugName() const { return mName.c_str(); } const LayerMetadata* LayerFE::getMetadata() const { return &mSnapshot->layerMetadata; } const LayerMetadata* LayerFE::getRelativeMetadata() const { return &mSnapshot->relativeLayerMetadata; } int32_t LayerFE::getSequence() const { return static_cast(mSnapshot->uniqueSequence); } bool LayerFE::hasRoundedCorners() const { return mSnapshot->roundedCorner.hasRoundedCorners(); } void LayerFE::setWasClientComposed(const sp& fence) { mCompositionResult.lastClientCompositionFence = fence; } bool LayerFE::hasBufferOrSidebandStream() const { return mSnapshot->externalTexture || mSnapshot->sidebandStream; } bool LayerFE::fillsColor() const { return mSnapshot->color.r >= 0.0_hf && mSnapshot->color.g >= 0.0_hf && mSnapshot->color.b >= 0.0_hf; } bool LayerFE::hasBlur() const { return mSnapshot->backgroundBlurRadius > 0 || mSnapshot->blurRegions.size() > 0; } bool LayerFE::drawShadows() const { return mSnapshot->shadowSettings.length > 0.f && (mSnapshot->shadowSettings.ambientColor.a > 0 || mSnapshot->shadowSettings.spotColor.a > 0); }; const sp LayerFE::getBuffer() const { return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr; } void LayerFE::setReleaseFence(const FenceResult& releaseFence) { // Promises should not be fulfilled more than once. This case can occur if virtual // displays with the same layerstack ID are being created and destroyed in quick // succession, such as in tests. This would result in a race condition in which // multiple displays have the same layerstack ID within the same vsync interval. if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) { return; } mReleaseFence.set_value(releaseFence); mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED; } // LayerFEs are reused and a new fence needs to be created whevever a buffer is latched. ftl::Future LayerFE::createReleaseFenceFuture() { if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) { LOG_ALWAYS_FATAL("Attempting to create a new promise while one is still unfulfilled."); } mReleaseFence = std::promise(); mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::INITIALIZED; return mReleaseFence.get_future(); } LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() { return mReleaseFencePromiseStatus; } } // namespace android