aosp11/12/13 Wallpaper Gaussian blur, frosted glass SurfaceFlinger layer principle – the second section Maxima framework actual combat

hi, fans and friends!
The previous blog has explained in detail the related interface in the system to realize the Gaussian blur related effect of the window, click here for details
https://blog.csdn.net/learnframework/article/details/130767893

1. Supplementary app-level implementation

More framework dry goods knowledge hands-on teaching

Log.i("qq group", "422901085");

Here is an additional addition to add another implementation method that can actually be implemented:

 if (getWindow().getRootSurfaceControl() != null & amp; & amp;
                        getWindow().getRootSurfaceControl() instanceof ViewRootImpl) {<!-- -->
                    SurfaceControl surfaceControl = ((ViewRootImpl)getWindow().getRootSurfaceControl()).getSurfaceControl();
                    SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
                    transaction.setBackgroundBlurRadius(surfaceControl,100);
                    transaction. apply();
                    android.util.Log.i("lsm","setBackgroundBlurRadius");
                }

In fact, it means that the key is to set the corresponding BackgroundBlurRadius on the surfacecontrol of this layer. The Dim layer analyzed in the previous section also sets the corresponding BackgroundBlurRadius
Let’s start to enter the app level and call setBackgroundBlurRadius, how does SurfaceFlinger handle it

2. Analysis of some source code principles of SurfaceFlinger

First look at the app’s setBackgroundBlurRadius interface

 /**
         * Sets the background blur radius of the {@link SurfaceControl}.
         *
         * @param sc SurfaceControl.
         * @param radius Blur radius in pixels.
         * @return itself.
         * @hide
         */
        public Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) {<!-- -->
            checkPreconditions(sc);
            nativeSetBackgroundBlurRadius(mNativeObject, sc.mNativeObject, radius);
            return this;
        }

You can see that the call is the nativeSetBackgroundBlurRadius method, which is a native method:

static void nativeSetBackgroundBlurRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint blurRadius) {<!-- -->
    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);

    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
    transaction->setBackgroundBlurRadius(ctrl, blurRadius);
}

What is called here is setBackgroundBlurRadius of SurfaceComposerClient::Transaction

SurfaceComposerClient::Transaction & SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
        const sp<SurfaceControl> & amp; sc, int backgroundBlurRadius) {<!-- -->
    layer_state_t* s = getLayerState(sc);
    if (!s) {<!-- -->
        mStatus = BAD_INDEX;
        return *this;
    }
    s->what |= layer_state_t::eBackgroundBlurRadiusChanged;
    s->backgroundBlurRadius = backgroundBlurRadius;
    return *this;
}

It is actually relatively simple here, that is, the backgroundBlurRadius of the layer_state_t structure is set

Here, the Layer.cpp of the surfaceflinger server is generally called at the end across the process.

bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {<!-- -->
    if (mDrawingState. backgroundBlurRadius == backgroundBlurRadius) return false;
    // If we start or stop drawing blur then the layer's visibility state may change so increment
    // the magic sequence number.
    if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) {<!-- -->
        mDrawingState.sequence++;
    }
    mDrawingState. backgroundBlurRadius = backgroundBlurRadius;
    mDrawingState.modified = true;
    setTransactionFlags(eTransactionNeeded);
    return true;
}

The main thing is to assign this backgroundBlurRadius to mDrawingState.backgroundBlurRadius

Let’s take a look at how to use this backgroundBlurRadius property in SurfaceFlinger
Finally, it was found that it was actually used when rendering in ./libs/renderengine/gl/GLESRenderEngine.cpp
In fact, it is also very easy to understand here, because Gaussian blur, which belongs to image processing, of course, can not be done by the CPU, but by the GPU, so it is easy to think of the Gaussian blur processing performed by the opengl rendering part

void GLESRenderEngine::drawLayersInternal(
        const std::shared_ptr<std::promise<RenderEngineResult>> & amp; & amp; resultPromise,
        const DisplaySettings & amp; display, const std::vector<LayerSettings> & amp; layers,
        const std::shared_ptr<ExternalTexture> & buffer, const bool useFramebufferCache,
        base::unique_fd & amp; & amp; bufferFence) {<!-- -->
    ATRACE_CALL();
    //omitted
    std::deque<const LayerSettings> blurLayers;
    if (CC_LIKELY(mBlurFilter != nullptr)) {<!-- -->
        for (const auto & amp; layer : layers) {<!-- -->
            if (layer.backgroundBlurRadius > 0) {<!-- --> //Traverse the layer of this backgroundBlurRadius
                blurLayers. push_back(layer);
            }
        }
    }
    const auto blurLayersSize = blurLayers. size();
//omitted

    for (const auto & amp; layer : layers) {<!-- --> //Traverse all layers for related rendering, note that this is the order from bottom to top
        if (blurLayers.size() > 0 & amp; & amp; blurLayers.front() == layer) {<!-- -->//If you traverse to the layer with blurbehind set
            blurLayers. pop_front();

            auto status = mBlurFilter->prepare(); //Prepare related fuzzy parameters
          
            if (blurLayers.size() == 0) {<!-- -->//Assuming that there is no more, then start to set the relevant buffer
                //Here is the key, the FrameBuffer that has been drawn will be obtained, and the data will be blurred later
                // Done blurring, time to bind the native FBO and render our blur onto it.
                fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
                                                                      buffer. get()
                                                                              ->getBuffer()
                                                                              ->getNativeBuffer(),
                                                                      useFramebufferCache);
                status = fbo->getStatus();
                setViewportAndProjection(display. physicalDisplay, display. clip);
            } else {<!-- -->
             //omit this case
            }
           
//do real blur
            status = mBlurFilter->render(blurLayersSize > 1);
        }
//omitted
        if (layer.source.buffer.buffer != nullptr) {<!-- -->//Normal layer rendering here
          //omitted
        }
//omitted
    return;
}

There are relatively many codes, here only need to understand the following key points:

1. When traversing layers, the bottom layer starts first. This needs attention
Why? Think about this is equivalent to surfaceflinger rendering each layer to a new screen canvas, of course, the bottom layer is drawn first

2. When a layer with blurbehind is recognized, the overall image of the previously rendered layer should be blurred, and then continue to draw the image of the previous layer