replicant-frameworks_native/services/surfaceflinger/LayerBlur.cpp
Steve Kondik 534f857430 surfaceflinger: Cleanups for blur
* Dynamically load it so no direct linkage or headers are required.

Change-Id: I4eba035f527ae4f0fa2485e24a8586145baf0adc
2016-08-01 13:08:02 -07:00

481 lines
15 KiB
C++

/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright (C) 2007 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
//#define LOG_NDEBUG 0
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <time.h>
#include <dlfcn.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <GLES/gl.h>
#include <GLES2/gl2.h>
#include "LayerBlur.h"
#include "SurfaceFlinger.h"
#include "DisplayDevice.h"
#include "RenderEngine/RenderEngine.h"
namespace android {
// ---------------------------------------------------------------------------
// Automatically disables scissor test and restores it when destroyed
class ScopedScissorDisabler {
bool scissorEnabled;
public:
ScopedScissorDisabler(bool enabled) : scissorEnabled(enabled) {
if(scissorEnabled) {
glDisable(GL_SCISSOR_TEST);
}
}
~ScopedScissorDisabler() {
if(scissorEnabled) {
glEnable(GL_SCISSOR_TEST);
}
};
};
static void setupMeshPartial(Mesh& mesh, Rect rcDraw, Rect rcTexture, int texWidth, int texHeight, int viewportHeight) {
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
position[0] = vec2(rcDraw.left, rcDraw.top);
position[1] = vec2(rcDraw.left, rcDraw.bottom);
position[2] = vec2(rcDraw.right, rcDraw.bottom);
position[3] = vec2(rcDraw.right, rcDraw.top);
for(size_t i=0; i<4; ++i) {
position[i].y = viewportHeight - position[i].y;
}
Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(rcTexture.left/(float)texWidth, 1.0f - rcTexture.top/(float)texHeight);
texCoords[1] = vec2(rcTexture.left/(float)texWidth, 1.0f - rcTexture.bottom/(float)texHeight);
texCoords[2] = vec2(rcTexture.right/(float)texWidth, 1.0f - rcTexture.bottom/(float)texHeight);
texCoords[3] = vec2(rcTexture.right/(float)texWidth, 1.0f - rcTexture.top/(float)texHeight);
}
static void setupMesh(Mesh& mesh, int width, int height, int viewportHeight) {
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
position[0] = vec2(0, 0);
position[1] = vec2(0, height);
position[2] = vec2(width, height);
position[3] = vec2(width, 0);
for(size_t i=0; i<4; ++i) {
position[i].y = viewportHeight - position[i].y;
}
Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(0, 1.0f);
texCoords[1] = vec2(0, 0);
texCoords[2] = vec2(1.0f, 0);
texCoords[3] = vec2(1.0f, 1.0f);
}
LayerBlur::LayerBlur(SurfaceFlinger* flinger, const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags)
: Layer(flinger, client, name, w, h, flags), mBlurMaskSampling(1),
mBlurMaskAlphaThreshold(0.0f) ,mLastFrameSequence(0)
{
GLuint texnames[3];
mFlinger->getRenderEngine().genTextures(3, texnames);
mTextureCapture.init(Texture::TEXTURE_2D, texnames[0]);
mTextureBlur.init(Texture::TEXTURE_2D, texnames[1]);
mTextureMasking.init(Texture::TEXTURE_2D, texnames[2]);
}
LayerBlur::~LayerBlur() {
releaseFbo(mFboCapture);
releaseFbo(mFboMasking);
mFlinger->deleteTextureAsync(mTextureCapture.getTextureName());
mFlinger->deleteTextureAsync(mTextureBlur.getTextureName());
mFlinger->deleteTextureAsync(mTextureMasking.getTextureName());
}
void LayerBlur::onDraw(const sp<const DisplayDevice>& hw, const Region& /*clip*/,
bool useIdentityTransform)
{
clock_t t1 = clock();
const ScopedTrace traceTotal(ATRACE_TAG, "Blur.onDraw");
const Layer::State& s(getDrawingState());
if (s.alpha==0) {
return;
}
/////
// NOTE:
//
// Scissor test has been turned on by SurfaceFlinger for NON-primary display
// We need to turn off the scissor test during our fbo drawing
GLboolean isScissorEnabled = false;
glGetBooleanv(GL_SCISSOR_TEST, &isScissorEnabled);
ScopedScissorDisabler _(isScissorEnabled);
//
/////
int hwWidth = hw->getWidth();
int hwHeight = hw->getHeight();
RenderEngine& engine(mFlinger->getRenderEngine());
bool savedProjectionYSwap = engine.getProjectionYSwap();
Rect savedProjectionSourceCrop = engine.getProjectionSourceCrop();
Transform::orientation_flags savedProjectionRotation = engine.getProjectionRotation();
size_t savedViewportWidth = engine.getViewportWidth();
size_t savedViewportHeight = engine.getViewportHeight();
if (mLastFrameSequence != mFlinger->mActiveFrameSequence ||
mTextureBlur.getWidth() == 0 || mTextureBlur.getHeight() == 0) {
// full drawing needed.
// capture
if (!captureScreen(hw, mFboCapture, mTextureCapture, hwWidth, hwHeight)) {
return;
}
// blur
size_t outTexWidth = mTextureBlur.getWidth();
size_t outTexHeight = mTextureBlur.getHeight();
if (mBlurImpl.blur(s.blur,
mTextureCapture.getTextureName(),
mTextureCapture.getWidth(),
mTextureCapture.getHeight(),
mTextureBlur.getTextureName(),
&outTexWidth,
&outTexHeight) != OK) {
return;
}
// mTextureBlur now has "Blurred image"
mTextureBlur.setDimensions(outTexWidth, outTexHeight);
} else {
// We can just re-use mTextureBlur.
// SurfaceFlinger or other LayerBlur object called my draw() multiple times
// while making one frame.
//
// Fall through
}
// masking
bool masking = false;
sp<Layer> maskLayer = mBlurMaskLayer.promote();
if (maskLayer != 0) {
// The larger sampling, the faster drawing.
// The smaller sampling, the prettier out line.
int sampling = mBlurMaskSampling >= 1 ? mBlurMaskSampling : 1;
//ALOGV("maskLayer available, sampling:%d", sampling);
masking = drawMaskLayer(maskLayer, hw, mFboMasking, hwWidth, hwHeight, sampling, mTextureMasking);
}
// final draw
doDrawFinal(hw,
savedViewportWidth, savedViewportHeight,
savedProjectionSourceCrop,
savedProjectionYSwap,
savedProjectionRotation,
useIdentityTransform,
masking ? &mTextureMasking : 0
);
mLastFrameSequence = mFlinger->mActiveFrameSequence;
clock_t t2 = clock();
ALOGV("onDraw took %d ms", (int)(1000*(t2-t1)/CLOCKS_PER_SEC));
}
bool LayerBlur::captureScreen(const sp<const DisplayDevice>& hw, FBO& fbo, Texture& texture, int width, int height) {
ATRACE_CALL();
ensureFbo(fbo, width, height, texture.getTextureName());
Transform::orientation_flags rotation = Transform::ROT_0;
if(fbo.fbo == 0) {
ALOGE("captureScreen(). fbo.fbo == 0");
return false;
}
GLint savedFramebuffer = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)fbo.fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
texture.getTextureTarget(),
texture.getTextureName(), 0);
mFlinger->getRenderEngine().clearWithColor(0.0f, 0.0f, 0.0f, 1.0f);
rotation = (Transform::orientation_flags)(rotation ^ hw->getPanelMountFlip());
mFlinger->renderScreenImplLocked(
hw,
Rect(0,0,width,height),
width, height,
0, getDrawingState().z-1,
false,
false,
rotation);
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
texture.setDimensions(width, height);
return true;
}
bool LayerBlur::drawMaskLayer(sp<Layer>& maskLayer, const sp<const DisplayDevice>& hw,
FBO& fbo, int width, int height, int sampling, Texture& texture) {
// Draw maskLayer into fbo
ATRACE_CALL();
int maskWidth = width/sampling;
int maskHeight = height/sampling;
ensureFbo(fbo, maskWidth, maskHeight, texture.getTextureName());
if(fbo.fbo == 0) {
ALOGE("drawMaskLayer(). fbo.fbo == 0");
return false;
}
GLint savedFramebuffer = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)fbo.fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
texture.getTextureTarget(),
texture.getTextureName(), 0);
mFlinger->getRenderEngine().setViewportAndProjection(
maskWidth, maskHeight,
Rect(0,0,width,height),
height,
false,
Transform::ROT_0
);
setupMesh(mMesh, width, height, height);
mFlinger->getRenderEngine().clearWithColor(0.0f, 0.0f, 0.0f, 0.0f); // alpha must be ZERO
maskLayer->draw(hw);
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
texture.setDimensions(maskWidth, maskHeight);
return true;
}
/*
* draw final texture into outer framebuffer
*/
void LayerBlur::doDrawFinal(const sp<const DisplayDevice>& hw,
int savedViewportWidth, int savedViewportHeight,
Rect savedProjectionSourceCrop,
bool savedProjectionYSwap,
Transform::orientation_flags savedRotation,
bool useIdentityTransform,
Texture* maskTexture
) {
ATRACE_CALL();
int hwWidth = hw->getWidth();
int hwHeight = hw->getHeight();
RenderEngine& engine(mFlinger->getRenderEngine());
const Layer::State& s(getDrawingState());
Transform trToDraw(useIdentityTransform ? hw->getTransform() : hw->getTransform() * s.transform);
Transform trToMapTexture(hw->getTransform() * s.transform);
Rect frameToDraw(trToDraw.transform(Rect(s.active.w, s.active.h)));
Rect frameToMapTexture(trToMapTexture.transform(Rect(s.active.w, s.active.h)));
engine.setViewportAndProjection(
savedViewportWidth, savedViewportHeight,
savedProjectionSourceCrop,
hwHeight,
savedProjectionYSwap,
savedRotation
);
const mat4 identity;
float textureMatrix[16];
memcpy(textureMatrix, identity.asArray(), sizeof(textureMatrix));
//mTextureBlur.setDimensions(hwWidth, hwHeight);
mTextureBlur.setFiltering(true);
mTextureBlur.setMatrix(textureMatrix);
if (maskTexture != 0) {
maskTexture->setFiltering(false);
maskTexture->setMatrix(textureMatrix);
}
setupMeshPartial(mMesh, frameToDraw, frameToMapTexture, hwWidth, hwHeight,
savedProjectionSourceCrop.height());
engine.setupLayerTexturing(mTextureBlur);
engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), s.alpha);
if (maskTexture) {
engine.setupLayerMasking(*maskTexture, mBlurMaskAlphaThreshold);
}
engine.drawMesh(mMesh);
engine.disableLayerMasking();
engine.disableBlending();
engine.disableTexturing();
}
bool LayerBlur::isVisible() const {
const Layer::State& s(getDrawingState());
return !(s.flags & layer_state_t::eLayerHidden) && s.alpha;
}
bool LayerBlur::setBlurMaskLayer(sp<Layer>& maskLayer) {
if (maskLayer == mBlurMaskLayer) {
return false;
}
mBlurMaskLayer = maskLayer;
return true;
}
void LayerBlur::initFbo(FBO& fbobj, int width, int height, int textureName) {
GLuint fbo=0;
glGenFramebuffers(1, &fbo);
glBindTexture(GL_TEXTURE_2D, textureName);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
GLint savedFramebuffer = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, textureName, 0);
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
fbobj.fbo = fbo;
fbobj.width = width;
fbobj.height = height;
}
void LayerBlur::releaseFbo(FBO& fbo) {
if(fbo.fbo != 0) {
glDeleteFramebuffers(1, (GLuint*)&fbo.fbo);
}
fbo.fbo = 0;
fbo.width = 0;
fbo.height = 0;
}
void LayerBlur::ensureFbo(FBO& fbo, int width, int height, int textureName) {
if(fbo.fbo != 0) {
if(fbo.width != width || fbo.height != height) {
releaseFbo(fbo);
}
}
if(fbo.fbo == 0) {
initFbo(fbo, width, height, textureName);
}
}
// ---------------------------------------------------------------------------
void* LayerBlur::BlurImpl::sLibHandle = NULL;
LayerBlur::BlurImpl::initBlurTokenFn LayerBlur::BlurImpl::initBlurToken = NULL;
LayerBlur::BlurImpl::releaseBlurTokenFn LayerBlur::BlurImpl::releaseBlurToken = NULL;
LayerBlur::BlurImpl::blurFn LayerBlur::BlurImpl::doBlur = NULL;
Mutex LayerBlur::BlurImpl::sLock;
void LayerBlur::BlurImpl::closeBlurImpl() {
if (sLibHandle != NULL) {
dlclose(sLibHandle);
sLibHandle = NULL;
}
}
status_t LayerBlur::BlurImpl::initBlurImpl() {
if (sLibHandle != NULL) {
return OK;
}
sLibHandle = dlopen("libuiblur.so", RTLD_NOW);
if (sLibHandle == NULL) {
return NO_INIT;
}
// happy happy joy joy!
initBlurToken = (initBlurTokenFn)dlsym(sLibHandle,
"_ZN7qtiblur13initBlurTokenEv");
releaseBlurToken = (releaseBlurTokenFn)dlsym(sLibHandle,
"_ZN7qtiblur16releaseBlurTokenEPv");
if (sizeof(size_t) == 4) {
doBlur = (blurFn)dlsym(sLibHandle,
"_ZN7qtiblur4blurEPvijjjjPjS1_");
} else if (sizeof(size_t) == 8) {
doBlur = (blurFn)dlsym(sLibHandle,
"_ZN7qtiblur4blurEPvijmmjPmS1_");
}
if (!initBlurToken || !releaseBlurToken || !doBlur) {
ALOGE("dlsym failed for blur impl!: %s", dlerror());
closeBlurImpl();
return NO_INIT;
}
return OK;
}
LayerBlur::BlurImpl::BlurImpl() {
Mutex::Autolock _l(sLock);
if (initBlurImpl() == OK) {
mToken = initBlurToken();
}
}
LayerBlur::BlurImpl::~BlurImpl() {
Mutex::Autolock _l(sLock);
if (mToken != NULL) {
releaseBlurToken(mToken);
}
}
status_t LayerBlur::BlurImpl::blur(int level, uint32_t inId, size_t inWidth, size_t inHeight,
uint32_t outId, size_t* outWidth, size_t* outHeight) {
Mutex::Autolock _l(sLock);
if (mToken == NULL) {
return NO_INIT;
}
return doBlur(mToken, level, inId, inWidth, inHeight,
outId, outWidth, outHeight) ? OK : NO_INIT;
}
// ---------------------------------------------------------------------------
}; // namespace android