Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 621c3b11 authored by Sherry Zhou's avatar Sherry Zhou
Browse files

Rain effect optimization

Remove rain splash can make GPU power to ~250mW, and merging glass rain to shower rain layer can avoid one texture reference, which can make GPU power to ~180mW.
Add back rain splash in patchset 6 with only generating outline frame
buffer, get ~250mW.

Test: manual test, before and after videos in b/393240176
Bug: 379336782
Flag: NONE Magic Portrait only

Change-Id: I6a002f9bd005b469e1309f810525dbb93292a1d4
parent e3c9fc34
Loading
Loading
Loading
Loading
+0 −105
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

uniform shader texture;
uniform float time;
uniform float screenAspectRatio;
uniform float gridScale;
uniform float2 screenSize;
uniform half intensity;

#include "shaders/constants.agsl"
#include "shaders/utils.agsl"
#include "shaders/glass_rain.agsl"
#include "shaders/rain_constants.agsl"

vec4 main(float2 fragCoord) {
    // 0. Calculate UV and add a bit of noise so that the droplets are not perfect circles.
    float2 uv = vec2(valueNoise(fragCoord) * 0.015 - 0.0025) + fragCoord / screenSize;

    // 1. Generate small glass rain.
    GlassRain smallDrippingRain = generateGlassRain(
         uv,
         screenAspectRatio,
         time * 0.7,
         /* Grid size = */ vec2(5.0, 1.6) * gridScale,
         intensity * 0.6);
    float dropMask = smallDrippingRain.dropMask;
    float droppletsMask = smallDrippingRain.droppletsMask;
    float trailMask = smallDrippingRain.trailMask;
    vec2 dropUvMasked = smallDrippingRain.drop * dropMask;
    vec2 droppletsUvMasked = smallDrippingRain.dropplets * droppletsMask;

    // 2. Generate medium size glass rain.
    GlassRain medDrippingRain = generateGlassRain(
          uv,
          screenAspectRatio,
          time * 0.80,
          /* Grid size = */ vec2(6., 0.945) * gridScale,
          intensity * 0.6);

    // 3. Combine those two glass rains.
    dropMask = max(medDrippingRain.dropMask, dropMask);
    droppletsMask = max(medDrippingRain.droppletsMask, droppletsMask);
    trailMask = max(medDrippingRain.trailMask, trailMask);
    dropUvMasked = mix(dropUvMasked,
        medDrippingRain.drop * medDrippingRain.dropMask, medDrippingRain.dropMask);
    droppletsUvMasked = mix(droppletsUvMasked,
        medDrippingRain.dropplets * medDrippingRain.droppletsMask, medDrippingRain.droppletsMask);

    // 4. Add static rain droplets on the glass surface. (They stay in place and dissapate.)
    vec2 gridSize = vec2(12., 12.) * gridScale;
    // Aspect ratio impacts visible cells.
    gridSize.y /= screenAspectRatio;
    vec3 staticRain = generateStaticGlassRain(uv, time, intensity, gridSize);
    dropMask = max(dropMask, staticRain.z);
    dropUvMasked = mix(dropUvMasked, staticRain.xy * staticRain.z, staticRain.z);

    // 5. Distort uv for the rain drops and dropplets.
    float distortionDrop = -0.1;
    vec2 uvDiffractionOffsets =
        distortionDrop * dropUvMasked;
    vec2 s = screenSize;
    // Ensure the diffracted image in drops is not inverted.
    s.y *= -1;

    vec4 color = texture.eval(fragCoord);
    vec3 sampledColor = texture.eval(fragCoord + uvDiffractionOffsets * s).rgb;
    color.rgb = mix(color.rgb, sampledColor, max(dropMask, droppletsMask));

    // 6. Add color tint to the rain drops.
    color.rgb = mix(
        color.rgb,
        dropTint,
        dropTintIntensity * smoothstep(0.7, 1., max(dropMask, droppletsMask)));

    // 7. Add highlight to the drops.
    color.rgb = mix(
        color.rgb,
        highlightColor,
        highlightIntensity
            * smoothstep(0.05, 0.08, max(dropUvMasked * 1.7, droppletsUvMasked * 2.6)).x);

    // 8. Add shadows to the drops.
    color.rgb = mix(
        color.rgb,
        contactShadowColor,
        dropShadowIntensity *
            smoothstep(0.055, 0.1, max(length(dropUvMasked * 1.7),
                length(droppletsUvMasked * 1.9))));

    return color;
}
 No newline at end of file
+0 −2
Original line number Diff line number Diff line
@@ -41,8 +41,6 @@ Rain generateRain(
    in float rainIntensity
) {
    /* Grid. */
    // Number of rows and columns (each one is a cell, a drop).
    float cellAspectRatio = rainGridSize.x / rainGridSize.y;
    // Aspect ratio impacts visible cells.
    uv.y /= screenAspectRatio;
    // scale the UV to allocate number of rows and columns.
+83 −6
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ uniform mat3 transformMatrixWeather;
#include "shaders/rain_shower.agsl"
#include "shaders/rain_constants.agsl"
#include "shaders/rain_splash.agsl"
#include "shaders/glass_rain.agsl"

// Controls how visible the rain drops are.
const float rainVisibility = 0.4;
@@ -79,6 +80,7 @@ vec3 drawSplashes(vec2 uv, vec2 fragCoord, vec3 color) {
}

vec4 main(float2 fragCoord) {
    // 1. Generate rain shower.
    // Apply transform matrix to fragCoord
    float2 uvTexture = transformPoint(transformMatrixBitmap, fragCoord);
    // Calculate uv for snow based on transformed coordinates
@@ -96,7 +98,7 @@ vec4 main(float2 fragCoord) {
    float variation = wiggle(time - uv.y * 1.1, 0.10);
    vec2 uvRot = rotateAroundPoint(uv, vec2(0.5, -1.42), variation * PI / 9.);

    // 1. Generate a layer of rain behind the subject.
    // 1.1. Generate a layer of rain behind the subject.
    Rain rain = generateRain(
          uvRot,
          screenAspectRatio,
@@ -106,7 +108,7 @@ vec4 main(float2 fragCoord) {

    color.rgb = mix(color.rgb, highlightColor, rainVisibility * rain.dropMask);

    // 2. Generate mid layer of rain behind the subject.
    // 1.2. Generate mid layer of rain behind the subject.
    rain = generateRain(
          uvRot,
          screenAspectRatio,
@@ -114,16 +116,16 @@ vec4 main(float2 fragCoord) {
          /* Grid size = */ vec2(30.0, 4.0) * gridScale,
          intensity);

    // 3. Blend those layers.
    // 1.3. Blend those layers.
    color.rgb = mix(color.rgb, highlightColor, rainVisibility * rain.dropMask);

    // 4. Blend with the foreground. Any effect from here will be in front of the subject.
    // 1.4. Blend with the foreground. Any effect from here will be in front of the subject.
    color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a);

    // 5. Draw splashes
    // (1.5. Draw splashes)
    color.rgb = drawSplashes(uv, fragCoord, color.rgb);

    // 6. Generate a layer of rain in front of the subject (bigger and faster).
    // 1.6. Generate a layer of rain in front of the subject (bigger and faster).
    rain = generateRain(
          uvRot,
          screenAspectRatio,
@@ -134,5 +136,80 @@ vec4 main(float2 fragCoord) {
    // Closer rain drops are less visible.
    color.rgb = mix(color.rgb, highlightColor, 0.7 * rainVisibility * rain.dropMask);

    // 2. Generate glass rain layer.
    // 2.0. Calculate UV and add a bit of noise so that the droplets are not perfect circles.
    float2 glassUv = vec2(valueNoise(fragCoord) * 0.015 - 0.0025) + fragCoord / screenSize;

    // 2.1. Generate small glass rain.
    GlassRain smallDrippingRain = generateGlassRain(
         glassUv,
         screenAspectRatio,
         time * 0.7,
         /* Grid size = */ vec2(5.0, 1.6) * gridScale,
         intensity * 0.6);
    float dropMask = smallDrippingRain.dropMask;
    float droppletsMask = smallDrippingRain.droppletsMask;
    float trailMask = smallDrippingRain.trailMask;
    vec2 dropUvMasked = smallDrippingRain.drop * dropMask;
    vec2 droppletsUvMasked = smallDrippingRain.dropplets * droppletsMask;

    // 2.2. Generate medium size glass rain.
    GlassRain medDrippingRain = generateGlassRain(
          glassUv,
          screenAspectRatio,
          time * 0.80,
          /* Grid size = */ vec2(6., 0.945) * gridScale,
          intensity * 0.6);

    // 2.3. Combine those two glass rains.
    dropMask = max(medDrippingRain.dropMask, dropMask);
    droppletsMask = max(medDrippingRain.droppletsMask, droppletsMask);
    trailMask = max(medDrippingRain.trailMask, trailMask);
    dropUvMasked = mix(dropUvMasked,
        medDrippingRain.drop * medDrippingRain.dropMask, medDrippingRain.dropMask);
    droppletsUvMasked = mix(droppletsUvMasked,
        medDrippingRain.dropplets * medDrippingRain.droppletsMask, medDrippingRain.droppletsMask);

    // 2.4. Add static rain droplets on the glass surface. (They stay in place and dissapate.)
    vec2 gridSize = vec2(12., 12.) * gridScale;
    // Aspect ratio impacts visible cells.
    gridSize.y /= screenAspectRatio;
    vec3 staticRain = generateStaticGlassRain(glassUv, time, intensity, gridSize);
    dropMask = max(dropMask, staticRain.z);
    dropUvMasked = mix(dropUvMasked, staticRain.xy * staticRain.z, staticRain.z);

    // 2.5. Distort uv for the rain drops and dropplets.
    float distortionDrop = -0.1;
    vec2 uvDiffractionOffsets =
        distortionDrop * dropUvMasked;
     vec2  s = screenSize;
    // Ensure the diffracted image in drops is not inverted.
    s.y *= -1;

     vec3 sampledColor = background.eval(uvTexture + uvDiffractionOffsets * s).rgb;
    sampledColor = imageRangeConversion(sampledColor, 0.84, 0.02, noise, intensity);
    color.rgb = mix(color.rgb, sampledColor, max(dropMask, droppletsMask));

    // 2.6. Add color tint to the rain drops.
    color.rgb = mix(
        color.rgb,
        dropTint,
        dropTintIntensity * smoothstep(0.7, 1., max(dropMask, droppletsMask)));

    // 2.7. Add highlight to the drops.
    color.rgb = mix(
        color.rgb,
        highlightColor,
        highlightIntensity
            * smoothstep(0.05, 0.08, max(dropUvMasked * 1.7, droppletsUvMasked * 2.6)).x);

    // 2.8. Add shadows to the drops.
    color.rgb = mix(
        color.rgb,
        contactShadowColor,
        dropShadowIntensity *
            smoothstep(0.055, 0.1, max(length(dropUvMasked * 1.7),
                length(droppletsUvMasked * 1.9))));

    return color;
}
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.SizeF
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTransformDifference
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.centerCropMatrix
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.getScale
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.invertAndTransposeMatrix
import kotlin.random.Random

@@ -47,6 +48,7 @@ abstract class WeatherEffectBase(
    // Apply to weather components not rely on image textures
    // Should be identity matrix in editor, and only change when parallax applied in homescreen
    private val transformMatrixWeather: FloatArray = FloatArray(9)
    protected var bitmapScale = getScale(centerCropMatrix)
    protected var elapsedTime: Float = 0f

    abstract val shader: RuntimeShader
@@ -56,6 +58,7 @@ abstract class WeatherEffectBase(

    override fun setMatrix(matrix: Matrix) {
        this.parallaxMatrix.set(matrix)
        bitmapScale = getScale(parallaxMatrix)
        adjustCropping(surfaceSize)
    }

@@ -111,6 +114,7 @@ abstract class WeatherEffectBase(
                SizeF(background.width.toFloat(), background.height.toFloat()),
            )
        parallaxMatrix.set(centerCropMatrix)
        bitmapScale = getScale(centerCropMatrix)
        shader.setInputBuffer(
            "background",
            BitmapShader(this.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
+24 −47
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.google.android.wallpaper.weathereffects.graphics.rain
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RenderEffect
import android.graphics.RuntimeShader
@@ -29,7 +28,6 @@ import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffectBase
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.SolidColorShader
import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils
import java.util.concurrent.Executor

@@ -46,12 +44,17 @@ class RainEffect(
) : WeatherEffectBase(foreground, background, surfaceSize) {

    private val rainPaint = Paint().also { it.shader = rainConfig.colorGradingShader }

    // Set blur effect to reduce the outline noise. No need to set blur effect every time we
    // re-generate the outline buffer.
    // Outline buffer is set with bitmap size, so we need to multiply blur radius by scale to get
    // consistent blur across different surface
    private var outlineBuffer =
        FrameBuffer(background.width, background.height).apply {
            setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP))
            setRenderEffect(
                RenderEffect.createBlurEffect(
                    2f / bitmapScale,
                    2f / bitmapScale,
                    Shader.TileMode.CLAMP,
                )
            )
        }
    private val outlineBufferPaint = Paint().also { it.shader = rainConfig.outlineShader }

@@ -67,43 +70,19 @@ class RainEffect(
        elapsedTime += TimeUtils.millisToSeconds(deltaMillis)

        rainConfig.rainShowerShader.setFloatUniform("time", elapsedTime)
        rainConfig.glassRainShader.setFloatUniform("time", elapsedTime)

        rainConfig.glassRainShader.setInputShader("texture", rainConfig.rainShowerShader)
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.glassRainShader)
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.rainShowerShader)
    }

    override fun draw(canvas: Canvas) {
        canvas.drawPaint(rainPaint)
    }

    override fun release() {
        super.release()
        outlineBuffer.close()
    }

    override fun setIntensity(intensity: Float) {
        super.setIntensity(intensity)
        rainConfig.glassRainShader.setFloatUniform("intensity", intensity)
        val thickness = 1f + intensity * 10f
        rainConfig.outlineShader.setFloatUniform("thickness", thickness)

        // Need to recreate the outline buffer as the uniform has changed.
        createOutlineBuffer()
    }

    override fun setBitmaps(foreground: Bitmap?, background: Bitmap): Boolean {
        if (!super.setBitmaps(foreground, background)) {
            return false
        }
        outlineBuffer.close()
        outlineBuffer =
            FrameBuffer(background.width, background.height).apply {
                setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP))
            }
        updateTextureUniforms()

        // Need to recreate the outline buffer as the outlineBuffer has changed due to background
        updateTextureUniforms()
        createOutlineBuffer()
        return true
    }
@@ -120,18 +99,6 @@ class RainEffect(
    override val colorGradingIntensity: Float
        get() = rainConfig.colorGradingIntensity

    override fun adjustCropping(newSurfaceSize: SizeF) {
        super.adjustCropping(newSurfaceSize)
        rainConfig.glassRainShader.setFloatUniform(
            "screenSize",
            newSurfaceSize.width,
            newSurfaceSize.height,
        )

        val screenAspectRatio = GraphicsUtils.getAspectRatio(newSurfaceSize)
        rainConfig.glassRainShader.setFloatUniform("screenAspectRatio", screenAspectRatio)
    }

    override fun updateTextureUniforms() {
        val foregroundBuffer =
            BitmapShader(super.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
@@ -144,7 +111,15 @@ class RainEffect(
        )
    }

    /**
     * It's necessary to create outline buffer only when bitmaps change, only intensity change won't
     * create a new one cause we refer to intensity in each cell when drawing splashes.
     */
    private fun createOutlineBuffer() {
        rainConfig.outlineShader.setFloatUniform(
            "thickness",
            MAX_RAIN_OUTLINE_THICKNESS / bitmapScale,
        )
        val canvas = outlineBuffer.beginDrawing()
        canvas.drawPaint(outlineBufferPaint)
        outlineBuffer.endDrawing()
@@ -162,8 +137,7 @@ class RainEffect(

    private fun prepareColorGrading() {
        // Initialize the buffer with black, so that we don't ever draw garbage buffer.
        rainConfig.glassRainShader.setInputShader("texture", SolidColorShader(Color.BLACK))
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.glassRainShader)
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.rainShowerShader)
        rainConfig.lut?.let {
            rainConfig.colorGradingShader.setInputShader(
                "lut",
@@ -176,6 +150,9 @@ class RainEffect(
        val widthScreenScale =
            GraphicsUtils.computeDefaultGridSize(newSurfaceSize, rainConfig.pixelDensity)
        rainConfig.rainShowerShader.setFloatUniform("gridScale", widthScreenScale)
        rainConfig.glassRainShader.setFloatUniform("gridScale", widthScreenScale)
    }

    companion object {
        const val MAX_RAIN_OUTLINE_THICKNESS = 11f
    }
}
Loading