android_frameworks_base/libs/hwui/LayerRenderer.cpp
Romain Guy 6217a71cd2 Fix performance issue in Launcher
Bug #3515248

The problem is caused by the fast path when compositing layers on screen.
The fast path draws a single quad using glDrawArrays() whereas the general
path draws an arbitrary mesh using glDrawElements(). It looks like there's
an issue in the driver since glDrawArrays() is significantly slower than
glDrawElements() for a quad (6 vertices!)

This change just gets rid of the fast path.

Change-Id: Ib2361253ec67f44a988270f76c183422f12ce537
2011-03-15 16:32:28 -07:00

276 lines
8.3 KiB
C++

/*
* Copyright (C) 2011 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_TAG "OpenGLRenderer"
#include <ui/Rect.h>
#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Properties.h"
#include "Rect.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Rendering
///////////////////////////////////////////////////////////////////////////////
void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo);
const float width = mLayer->layer.getWidth();
const float height = mLayer->layer.getHeight();
#if RENDER_LAYERS_AS_REGIONS
Rect dirty(left, top, right, bottom);
if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 &&
dirty.right >= width && dirty.bottom >= height)) {
mLayer->region.clear();
dirty.set(0.0f, 0.0f, width, height);
} else {
dirty.intersect(0.0f, 0.0f, width, height);
android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
mLayer->region.subtractSelf(r);
}
OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
#else
OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque);
#endif
}
void LayerRenderer::finish() {
OpenGLRenderer::finish();
generateMesh();
LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->fbo);
// No need to unbind our FBO, this will be taken care of by the caller
// who will invoke OpenGLRenderer::resume()
}
GLint LayerRenderer::getTargetFbo() {
return mLayer->fbo;
}
///////////////////////////////////////////////////////////////////////////////
// Dirty region tracking
///////////////////////////////////////////////////////////////////////////////
bool LayerRenderer::hasLayer() {
return true;
}
Region* LayerRenderer::getRegion() {
#if RENDER_LAYERS_AS_REGIONS
if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
return OpenGLRenderer::getRegion();
}
return &mLayer->region;
#else
return OpenGLRenderer::getRegion();
#endif
}
void LayerRenderer::generateMesh() {
#if RENDER_LAYERS_AS_REGIONS
#if RENDER_LAYERS_RECT_AS_RECT
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
#else
if (mLayer->region.isEmpty()) {
#endif
if (mLayer->mesh) {
delete mLayer->mesh;
delete mLayer->meshIndices;
mLayer->mesh = NULL;
mLayer->meshIndices = NULL;
mLayer->meshElementCount = 0;
}
return;
}
size_t count;
const android::Rect* rects = mLayer->region.getArray(&count);
GLsizei elementCount = count * 6;
if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
delete mLayer->mesh;
delete mLayer->meshIndices;
mLayer->mesh = NULL;
mLayer->meshIndices = NULL;
}
bool rebuildIndices = false;
if (!mLayer->mesh) {
mLayer->mesh = new TextureVertex[count * 4];
mLayer->meshIndices = new uint16_t[elementCount];
rebuildIndices = true;
}
mLayer->meshElementCount = elementCount;
const float texX = 1.0f / float(mLayer->width);
const float texY = 1.0f / float(mLayer->height);
const float height = mLayer->layer.getHeight();
TextureVertex* mesh = mLayer->mesh;
uint16_t* indices = mLayer->meshIndices;
for (size_t i = 0; i < count; i++) {
const android::Rect* r = &rects[i];
const float u1 = r->left * texX;
const float v1 = (height - r->top) * texY;
const float u2 = r->right * texX;
const float v2 = (height - r->bottom) * texY;
TextureVertex::set(mesh++, r->left, r->top, u1, v1);
TextureVertex::set(mesh++, r->right, r->top, u2, v1);
TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
if (rebuildIndices) {
uint16_t quad = i * 4;
int index = i * 6;
indices[index ] = quad; // top-left
indices[index + 1] = quad + 1; // top-right
indices[index + 2] = quad + 2; // bottom-left
indices[index + 3] = quad + 2; // bottom-left
indices[index + 4] = quad + 1; // top-right
indices[index + 5] = quad + 3; // bottom-right
}
}
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Layers management
///////////////////////////////////////////////////////////////////////////////
Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height);
GLuint fbo = Caches::getInstance().fboCache.get();
if (!fbo) {
LOGW("Could not obtain an FBO");
return NULL;
}
glActiveTexture(GL_TEXTURE0);
Layer* layer = Caches::getInstance().layerCache.get(width, height);
if (!layer) {
LOGW("Could not obtain a layer");
return NULL;
}
layer->fbo = fbo;
layer->layer.set(0.0f, 0.0f, width, height);
layer->texCoords.set(0.0f, height / float(layer->height),
width / float(layer->width), 0.0f);
layer->alpha = 255;
layer->mode = SkXfermode::kSrcOver_Mode;
layer->blend = !isOpaque;
layer->colorFilter = NULL;
layer->region.clear();
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
glBindTexture(GL_TEXTURE_2D, layer->texture);
// Initialize the texture if needed
if (layer->empty) {
layer->empty = false;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (glGetError() != GL_NO_ERROR) {
LOGD("Could not allocate texture");
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
glDeleteTextures(1, &layer->texture);
Caches::getInstance().fboCache.put(fbo);
delete layer;
return NULL;
}
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->texture, 0);
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
return layer;
}
bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
if (layer) {
LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height);
if (Caches::getInstance().layerCache.resize(layer, width, height)) {
layer->layer.set(0.0f, 0.0f, width, height);
layer->texCoords.set(0.0f, height / float(layer->height),
width / float(layer->width), 0.0f);
} else {
if (layer->texture) glDeleteTextures(1, &layer->texture);
delete layer;
return false;
}
}
return true;
}
void LayerRenderer::destroyLayer(Layer* layer) {
if (layer) {
LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo);
if (layer->fbo) {
Caches::getInstance().fboCache.put(layer->fbo);
}
if (!Caches::getInstance().layerCache.put(layer)) {
if (layer->texture) glDeleteTextures(1, &layer->texture);
delete layer;
} else {
layer->region.clear();
}
}
}
void LayerRenderer::destroyLayerDeferred(Layer* layer) {
if (layer) {
LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->fbo);
Caches::getInstance().deleteLayerDeferred(layer);
}
}
}; // namespace uirenderer
}; // namespace android