blob: fb29fbb48beedccdc5455a09522d1a6ed6c674f4 [file] [log] [blame]
/*
* Copyright 2019 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "LensBlurFilter.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#include <ui/GraphicTypes.h>
#include <cstdint>
#include <utils/Trace.h>
namespace android {
namespace renderengine {
namespace gl {
// Number of blur samples in shader (for loop)
static constexpr auto kNumSamples = 12;
LensBlurFilter::LensBlurFilter(GLESRenderEngine& engine)
: BlurFilter(engine),
mVerticalDiagonalPassFbo(engine, true /* multiTarget */),
mVerticalDiagonalProgram(engine),
mCombinedProgram(engine) {
mVerticalDiagonalProgram.compile(getVertexShader(), getFragmentShader(false));
mCombinedProgram.compile(getVertexShader(), getFragmentShader(true));
mVDPosLoc = mVerticalDiagonalProgram.getAttributeLocation("aPosition");
mVDUvLoc = mVerticalDiagonalProgram.getAttributeLocation("aUV");
mVDTexture0Loc = mVerticalDiagonalProgram.getUniformLocation("uTexture0");
mVDSizeLoc = mVerticalDiagonalProgram.getUniformLocation("uSize");
mVDRadiusLoc = mVerticalDiagonalProgram.getUniformLocation("uRadius");
mVDNumSamplesLoc = mVerticalDiagonalProgram.getUniformLocation("uNumSamples");
mCPosLoc = mCombinedProgram.getAttributeLocation("aPosition");
mCUvLoc = mCombinedProgram.getAttributeLocation("aUV");
mCTexture0Loc = mCombinedProgram.getUniformLocation("uTexture0");
mCTexture1Loc = mCombinedProgram.getUniformLocation("uTexture1");
mCSizeLoc = mCombinedProgram.getUniformLocation("uSize");
mCRadiusLoc = mCombinedProgram.getUniformLocation("uRadius");
mCNumSamplesLoc = mCombinedProgram.getUniformLocation("uNumSamples");
}
void LensBlurFilter::allocateTextures() {
mVerticalDiagonalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(),
mBlurredFbo.getBufferHeight());
}
status_t LensBlurFilter::prepare() {
ATRACE_NAME("LensBlurFilter::prepare");
if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Invalid vertical-diagonal FBO");
return mVerticalDiagonalPassFbo.getStatus();
}
if (!mVerticalDiagonalProgram.isValid()) {
ALOGE("Invalid vertical-diagonal shader");
return GL_INVALID_OPERATION;
}
if (!mCombinedProgram.isValid()) {
ALOGE("Invalid blur shader");
return GL_INVALID_OPERATION;
}
// First, we'll apply the vertical/diagonal pass, that receives the flattened background layers,
// and writes the output to two textures (vertical and diagonal.)
mVerticalDiagonalPassFbo.bind();
mVerticalDiagonalProgram.useProgram();
// set uniforms
auto width = mVerticalDiagonalPassFbo.getBufferWidth();
auto height = mVerticalDiagonalPassFbo.getBufferHeight();
auto radiusF = fmax(1.0f, mRadius * kFboScale);
glViewport(0, 0, width, height);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
glUniform1i(mVDTexture0Loc, 0);
glUniform2f(mVDSizeLoc, mDisplayWidth, mDisplayHeight);
glUniform1f(mVDRadiusLoc, radiusF);
glUniform1i(mVDNumSamplesLoc, kNumSamples);
mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
drawMesh(mVDUvLoc, mVDPosLoc);
// Now we'll combine the multi render pass into a blurred image
mBlurredFbo.bind();
mCombinedProgram.useProgram();
// set uniforms
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getTextureName());
glUniform1i(mCTexture0Loc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getSecondaryTextureName());
glUniform1i(mCTexture1Loc, 1);
glUniform2f(mCSizeLoc, mDisplayWidth, mDisplayHeight);
glUniform1f(mCRadiusLoc, radiusF);
glUniform1i(mCNumSamplesLoc, kNumSamples);
mEngine.checkErrors("Setting vertical pass uniforms");
drawMesh(mCUvLoc, mCPosLoc);
// reset active texture
mBlurredFbo.unbind();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
// unbind program
glUseProgram(0);
return NO_ERROR;
}
string LensBlurFilter::getFragmentShader(bool forComposition) const {
string shader = "#version 310 es\n#define DIRECTION ";
shader += (forComposition ? "1" : "0");
shader += R"SHADER(
precision mediump float;
#define PI 3.14159265359
uniform sampler2D uTexture0;
uniform vec2 uSize;
uniform float uRadius;
uniform int uNumSamples;
highp in vec2 vUV;
#if DIRECTION == 0
layout(location = 0) out vec4 fragColor0;
layout(location = 1) out vec4 fragColor1;
#else
uniform sampler2D uTexture1;
out vec4 fragColor;
#endif
const vec2 verticalMult = vec2(cos(PI / 2.0), sin(PI / 2.0));
const vec2 diagonalMult = vec2(cos(-PI / 6.0), sin(-PI / 6.0));
const vec2 diagonal2Mult = vec2(cos(-5.0 * PI / 6.0), sin(-5.0 * PI / 6.0));
vec3 blur(const sampler2D tex, vec2 uv, const vec2 direction, float radius,
int samples, float intensity) {
vec3 finalColor = vec3(0.0);
uv += direction * 0.5;
for (int i = 0; i < samples; i++){
float delta = radius * float(i) / float(samples);
vec3 color = texture(tex, uv + direction * delta).rgb;
color.rgb *= intensity;
finalColor += color;
}
return finalColor / float(samples);
}
vec3 blur(const sampler2D tex, vec2 uv, const vec2 direction, float radius,
int samples) {
return blur(tex, uv, direction, radius, samples, 1.0);
}
vec4[2] verticalDiagonalLensBlur (vec2 uv, sampler2D texture, vec2 resolution,
float radius, int samples) {
// Vertical Blur
vec2 blurDirV = 1.0 / resolution.xy * verticalMult;
vec3 colorV = blur(texture, uv, blurDirV, radius, samples);
// Diagonal Blur
vec2 blurDirD = 1.0 / resolution.xy * diagonalMult;
vec3 colorD = blur(texture, uv, blurDirD, radius, samples);
vec4 composed[2];
composed[0] = vec4(colorV, 1.0);
// added * 0.5, to remap
composed[1] = vec4((colorD + colorV) * 0.5, 1.0);
return composed;
}
vec4 rhombiLensBlur (vec2 uv, sampler2D texture0, sampler2D texture1, vec2 resolution,
float radius, int samples) {
vec2 blurDirection1 = 1.0 / resolution.xy * diagonalMult;
vec3 color1 = blur(texture0, uv, blurDirection1, radius, samples);
vec2 blurDirection2 = 1.0 / resolution.xy * diagonal2Mult;
vec3 color2 = blur(texture1, uv, blurDirection2, radius, samples, 2.0);
return vec4((color1 + color2) * 0.33, 1.0);
}
void main() {
#if DIRECTION == 0
// First pass: outputs two textures
vec4 colorOut[] = verticalDiagonalLensBlur(vUV, uTexture0, uSize, uRadius, uNumSamples);
fragColor0 = colorOut[0];
fragColor1 = colorOut[1];
#else
// Second pass: combines both textures into a blurred one.
fragColor = rhombiLensBlur(vUV, uTexture0, uTexture1, uSize, uRadius, uNumSamples);
#endif
}
)SHADER";
return shader;
}
} // namespace gl
} // namespace renderengine
} // namespace android