| /* | |
| * 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 <common/trace.h> | |
| #include <gui/GLConsumer.h> | |
| #include <math/vec3.h> | |
| #include <system/window.h> | |
| #include "LayerFE.h" | |
| #include "SurfaceFlinger.h" | |
| #include "ui/FenceResult.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<renderengine::ExternalTexture>& 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<float>(buffer->getWidth()), | |
| static_cast<float>(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 (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<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientComposition( | |
| compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { | |
| std::optional<compositionengine::LayerFE::LayerSettings> 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); | |
| layerSettings->borderSettings = mSnapshot->borderSettings; | |
| layerSettings->boxShadowSettings = mSnapshot->boxShadowSettings; | |
| return layerSettings; | |
| } | |
| std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal( | |
| compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { | |
| SFTRACE_CALL(); | |
| compositionengine::LayerFE::LayerSettings layerSettings; | |
| layerSettings.geometry.originalBounds = mSnapshot->geomLayerBounds; | |
| if (mSnapshot->parentRoundedCorner.hasRequestedRadius()) { | |
| layerSettings.geometry.otherRoundedCornersRadius = mSnapshot->parentRoundedCorner.radius; | |
| layerSettings.geometry.otherCrop = mSnapshot->parentRoundedCorner.cropRect; | |
| } else { | |
| layerSettings.geometry.otherCrop = mSnapshot->parentGeomLayerCrop; | |
| } | |
| 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<ui::Dataspace>( | |
| (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.backgroundBlurScale = mSnapshot->backgroundBlurScale; | |
| layerSettings.blurRegions = mSnapshot->blurRegions; | |
| layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4(); | |
| break; | |
| case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly: | |
| layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; | |
| layerSettings.backgroundBlurScale = mSnapshot->backgroundBlurScale; | |
| 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; | |
| layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect; | |
| // Record the name of the layer for debugging further down the stack. | |
| layerSettings.name = mSnapshot->name; | |
| layerSettings.luts = mSnapshot->luts ? mSnapshot->luts : targetSettings.luts; | |
| 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; | |
| layerSettings.sequence = -1; | |
| // 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() || hasBorderSettings() || hasBoxShadowSettings()) { | |
| layerSettings.skipContentDraw = true; | |
| } | |
| } | |
| void LayerFE::prepareBufferStateClientComposition( | |
| compositionengine::LayerFE::LayerSettings& layerSettings, | |
| compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const { | |
| SFTRACE_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 = (mSnapshot->hasProtectedContent && !targetSettings.isProtected) || | |
| (mSnapshot->isSecure && !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(); | |
| layerSettings.sequence = mSnapshot->sequence; | |
| 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::getFrontInternalDisplayRotationFlags(); | |
| 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<const float*>(textureMatrix)) * tr); | |
| memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix)); | |
| } | |
| const Rect win{layerSettings.geometry.boundaries}; | |
| float bufferWidth = static_cast<float>(mSnapshot->bufferSize.getWidth()); | |
| float bufferHeight = static_cast<float>(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<const float*>(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; | |
| } | |
| // The light source should be at (screenWidth/2, globalShadowSettings.lightPos.y) in | |
| // screenspace. | |
| vec2 lightPosScreenSpace = { | |
| (static_cast<float>(layerStackRect.width()) / 2.f), | |
| state.lightPos.y, | |
| }; | |
| // Skia expects light pos in layer space. | |
| vec2 lightPosLayerSpace = mSnapshot->geomInverseLayerTransform.transform(lightPosScreenSpace); | |
| state.lightPos.x = lightPosLayerSpace.x; | |
| state.lightPos.y = lightPosLayerSpace.y; | |
| caster.shadow = state; | |
| } | |
| void LayerFE::onPictureProfileCommitted() { | |
| mCompositionResult.wasPictureProfileCommitted = true; | |
| mCompositionResult.pictureProfileHandle = mSnapshot->pictureProfileHandle; | |
| } | |
| CompositionResult LayerFE::stealCompositionResult() { | |
| CompositionResult result; | |
| std::swap(mCompositionResult, result); | |
| return result; | |
| } | |
| 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<int32_t>(mSnapshot->uniqueSequence); | |
| } | |
| bool LayerFE::hasRoundedCorners() const { | |
| return mSnapshot->roundedCorner.hasRoundedCorners(); | |
| } | |
| void LayerFE::setWasClientComposed(const sp<Fence>& 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::hasBorderSettings() const { | |
| return mSnapshot->hasBorderSettings(); | |
| } | |
| bool LayerFE::hasBoxShadowSettings() const { | |
| return mSnapshot->hasBoxShadowSettings(); | |
| } | |
| bool LayerFE::drawShadows() const { | |
| return mSnapshot->shadowSettings.length > 0.f && | |
| (mSnapshot->shadowSettings.ambientColor.a > 0 || | |
| mSnapshot->shadowSettings.spotColor.a > 0); | |
| }; | |
| const sp<GraphicBuffer> 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; | |
| } | |
| if (releaseFence.has_value()) { | |
| if (FlagManager::getInstance().monitor_buffer_fences()) { | |
| if (auto strongBuffer = mReleasedBuffer.promote()) { | |
| strongBuffer->getDependencyMonitor() | |
| .addAccessCompletion(FenceTime::makeValid(releaseFence.value()), "HWC"); | |
| } | |
| } | |
| } | |
| 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<FenceResult> LayerFE::createReleaseFenceFuture() { | |
| if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) { | |
| LOG_ALWAYS_FATAL("Attempting to create a new promise while one is still unfulfilled."); | |
| } | |
| mReleaseFence = std::promise<FenceResult>(); | |
| mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::INITIALIZED; | |
| return mReleaseFence.get_future(); | |
| } | |
| LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() { | |
| return mReleaseFencePromiseStatus; | |
| } | |
| void LayerFE::setReleasedBuffer(sp<GraphicBuffer> buffer) { | |
| mReleasedBuffer = std::move(buffer); | |
| } | |
| void LayerFE::setLastHwcState(const LayerFE::HwcLayerDebugState &state) { | |
| mLastHwcState = state; | |
| } | |
| const LayerFE::HwcLayerDebugState& LayerFE::getLastHwcState() const { | |
| return mLastHwcState; | |
| }; | |
| } // namespace android |