Avoid multiple font cache texture uploads

Bug #8378964

This change defers drawing into layers until after the renderer for FBO0
is ready to draw. At that point, all the precaching is done which means
all glyphs can be uploaded at once in the font caches.

Change-Id: Ie1f7a7ff30f76f06fb3dbc72c7d05e66207d1ecb
This commit is contained in:
Romain Guy
2013-03-26 15:05:58 -07:00
parent 7c566bf3e4
commit 96885eb480
7 changed files with 181 additions and 31 deletions

View File

@@ -428,6 +428,8 @@ public abstract class HardwareRenderer {
interface HardwareDrawCallbacks {
/**
* Invoked before a view is drawn by a hardware renderer.
* This method can be used to apply transformations to the
* canvas but no drawing command should be issued.
*
* @param canvas The Canvas used to render the view.
*/
@@ -435,6 +437,7 @@ public abstract class HardwareRenderer {
/**
* Invoked after a view is drawn by a hardware renderer.
* It is safe to invoke drawing commands from this method.
*
* @param canvas The Canvas used to render the view.
*/

View File

@@ -52,6 +52,8 @@ public:
kOpBatch_Count, // Add other batch ids before this
};
void clear();
bool isEmpty() { return mBatches.isEmpty(); }
/**
@@ -78,8 +80,6 @@ private:
*/
void resetBatchingState();
void clear();
void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
void storeRestoreToCountBarrier(int newSaveCount);

View File

@@ -18,6 +18,8 @@
#include <utils/Log.h>
#include "DisplayList.h"
#include "DeferredDisplayList.h"
#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
@@ -43,15 +45,18 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
fbo = 0;
stencil = NULL;
debugDrawUpdate = false;
deferredList = NULL;
Caches::getInstance().resourceCache.incrementRefcount(this);
}
Layer::~Layer() {
if (mesh) delete mesh;
if (meshIndices) delete meshIndices;
if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
removeFbo();
deleteTexture();
delete[] mesh;
delete[] meshIndices;
delete deferredList;
}
uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
@@ -133,5 +138,43 @@ void Layer::setColorFilter(SkiaColorFilter* filter) {
}
}
void Layer::defer() {
if (!deferredList) {
deferredList = new DeferredDisplayList;
}
DeferStateStruct deferredState(*deferredList, *renderer,
DisplayList::kReplayFlag_ClipChildren);
const float width = layer.getWidth();
const float height = layer.getHeight();
if (dirtyRect.isEmpty() || (dirtyRect.left <= 0 && dirtyRect.top <= 0 &&
dirtyRect.right >= width && dirtyRect.bottom >= height)) {
dirtyRect.set(0, 0, width, height);
}
renderer->initViewport(width, height);
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
displayList->defer(deferredState, 0);
}
void Layer::flush() {
if (deferredList && !deferredList->isEmpty()) {
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
deferredList->flush(*renderer, dirtyRect);
renderer->finish();
renderer = NULL;
dirtyRect.setEmpty();
deferredList->clear();
}
}
}; // namespace uirenderer
}; // namespace android

View File

@@ -42,6 +42,8 @@ namespace uirenderer {
// Forward declarations
class OpenGLRenderer;
class DisplayList;
class DeferredDisplayList;
class DeferStateStruct;
/**
* A layer has dimensions and is backed by an OpenGL texture or FBO.
@@ -271,6 +273,9 @@ struct Layer {
return transform;
}
void defer();
void flush();
/**
* Bounds of the layer.
*/
@@ -379,6 +384,12 @@ private:
*/
mat4 transform;
/**
* Used to defer display lists when the layer is updated with a
* display list.
*/
DeferredDisplayList* deferredList;
}; // struct Layer
}; // namespace uirenderer

View File

@@ -129,8 +129,8 @@ Region* LayerRenderer::getRegion() const {
void LayerRenderer::generateMesh() {
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
if (mLayer->mesh) {
delete mLayer->mesh;
delete mLayer->meshIndices;
delete[] mLayer->mesh;
delete[] mLayer->meshIndices;
mLayer->mesh = NULL;
mLayer->meshIndices = NULL;
@@ -153,8 +153,8 @@ void LayerRenderer::generateMesh() {
GLsizei elementCount = count * 6;
if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
delete mLayer->mesh;
delete mLayer->meshIndices;
delete[] mLayer->mesh;
delete[] mLayer->meshIndices;
mLayer->mesh = NULL;
mLayer->meshIndices = NULL;

View File

@@ -120,6 +120,7 @@ OpenGLRenderer::OpenGLRenderer():
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
mFirstSnapshot = new Snapshot;
mFrameStarted = false;
mScissorOptimizationDisabled = false;
}
@@ -179,14 +180,11 @@ void OpenGLRenderer::initViewport(int width, int height) {
mFirstSnapshot->viewport.set(0, 0, width, height);
}
status_t OpenGLRenderer::prepare(bool opaque) {
return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
}
status_t OpenGLRenderer::prepareDirty(float left, float top,
void OpenGLRenderer::setupFrameState(float left, float top,
float right, float bottom, bool opaque) {
mCaches.clearGarbage();
mOpaque = opaque;
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->fbo = getTargetFbo();
@@ -194,13 +192,17 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
mSnapshot->setClip(left, top, right, bottom);
mTilingClip.set(left, top, right, bottom);
}
status_t OpenGLRenderer::startFrame() {
if (mFrameStarted) return DrawGlInfo::kStatusDone;
mFrameStarted = true;
mDirtyClip = true;
updateLayers();
discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
discardFramebuffer(left, top, right, bottom);
syncState();
glViewport(0, 0, mWidth, mHeight);
// Functors break the tiling extension in pretty spectacular ways
// This ensures we don't use tiling when a functor is going to be
@@ -211,7 +213,30 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
debugOverdraw(true, true);
return clear(left, top, right, bottom, opaque);
return clear(mTilingClip.left, mTilingClip.top,
mTilingClip.right, mTilingClip.bottom, mOpaque);
}
status_t OpenGLRenderer::prepare(bool opaque) {
return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
}
status_t OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
setupFrameState(left, top, right, bottom, opaque);
// Layer renderers will start the frame immediately
// The framebuffer renderer will first defer the display list
// for each layer and wait until the first drawing command
// to start the frame
if (mSnapshot->fbo == 0) {
syncState();
updateLayers();
} else {
return startFrame();
}
return DrawGlInfo::kStatusDone;
}
void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
@@ -241,8 +266,6 @@ status_t OpenGLRenderer::clear(float left, float top, float right, float bottom,
}
void OpenGLRenderer::syncState() {
glViewport(0, 0, mWidth, mHeight);
if (mCaches.blend) {
glEnable(GL_BLEND);
} else {
@@ -312,6 +335,8 @@ void OpenGLRenderer::finish() {
}
#endif
}
mFrameStarted = false;
}
void OpenGLRenderer::interrupt() {
@@ -503,8 +528,8 @@ void OpenGLRenderer::renderOverdraw() {
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
OpenGLRenderer* renderer = layer->renderer;
if (layer->deferredUpdateScheduled && layer->renderer &&
layer->displayList && layer->displayList->isRenderable()) {
Rect& dirty = layer->dirtyRect;
if (inFrame) {
@@ -512,19 +537,29 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
debugOverdraw(false, false);
}
renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
renderer->finish();
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
OpenGLRenderer* renderer = layer->renderer;
renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom,
!layer->isBlend());
renderer->drawDisplayList(layer->displayList, dirty,
DisplayList::kReplayFlag_ClipChildren);
renderer->finish();
} else {
layer->defer();
}
if (inFrame) {
resumeAfterLayer();
startTiling(mSnapshot);
}
dirty.setEmpty();
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
dirty.setEmpty();
layer->renderer = NULL;
}
layer->deferredUpdateScheduled = false;
layer->renderer = NULL;
layer->displayList = NULL;
layer->debugDrawUpdate = mCaches.debugLayersUpdates;
@@ -535,19 +570,54 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
}
void OpenGLRenderer::updateLayers() {
// If draw deferring is enabled this method will simply defer
// the display list of each individual layer. The layers remain
// in the layer updates list which will be cleared by flushLayers().
int count = mLayerUpdates.size();
if (count > 0) {
startMark("Layer Updates");
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
startMark("Layer Updates");
} else {
startMark("Defer Layer Updates");
}
// Note: it is very important to update the layers in reverse order
for (int i = count - 1; i >= 0; i--) {
Layer* layer = mLayerUpdates.itemAt(i);
updateLayer(layer, false);
mCaches.resourceCache.decrementRefcount(layer);
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
mCaches.resourceCache.decrementRefcount(layer);
}
}
mLayerUpdates.clear();
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
mLayerUpdates.clear();
glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
}
endMark();
}
}
void OpenGLRenderer::flushLayers() {
int count = mLayerUpdates.size();
if (count > 0) {
startMark("Apply Layer Updates");
char layerName[12];
// Note: it is very important to update the layers in reverse order
for (int i = count - 1; i >= 0; i--) {
sprintf(layerName, "Layer #%d", i);
startMark(layerName); {
Layer* layer = mLayerUpdates.itemAt(i);
layer->flush();
mCaches.resourceCache.decrementRefcount(layer);
}
endMark();
}
mLayerUpdates.clear();
glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
endMark();
}
}
@@ -1832,6 +1902,7 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
// will be performed by the display list itself
if (displayList && displayList->isRenderable()) {
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
displayList->replay(replayStruct, 0);
return replayStruct.mDrawGlStatus;
@@ -1840,6 +1911,10 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
DeferredDisplayList deferredList;
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
displayList->defer(deferStruct, 0);
flushLayers();
startFrame();
return deferredList.flush(*this, dirty);
}

View File

@@ -361,6 +361,18 @@ protected:
*/
void initViewport(int width, int height);
/**
* Perform the setup specific to a frame. This method does not
* issue any OpenGL commands.
*/
void setupFrameState(float left, float top, float right, float bottom, bool opaque);
/**
* Indicates the start of rendering. This method will setup the
* initial OpenGL state (viewport, clearing the buffer, etc.)
*/
status_t startFrame();
/**
* Clears the underlying surface if needed.
*/
@@ -897,6 +909,7 @@ private:
bool updateLayer(Layer* layer, bool inFrame);
void updateLayers();
void flushLayers();
/**
* Renders the specified region as a series of rectangles. This method
@@ -948,6 +961,10 @@ private:
sp<Snapshot> mSnapshot;
// State used to define the clipping region
Rect mTilingClip;
// Is the target render surface opaque
bool mOpaque;
// Is a frame currently being rendered
bool mFrameStarted;
// Used to draw textured quads
TextureVertex mMeshVertices[4];
@@ -996,6 +1013,7 @@ private:
String8 mName;
friend class DisplayListRenderer;
friend class Layer;
friend class TextSetupFunctor;
}; // class OpenGLRenderer