Merge "Allow layers with a stencil buffer to be resized on the fly Bug #7146141"

This commit is contained in:
Romain Guy
2013-01-19 01:03:27 +00:00
committed by Android (Google) Code Review
8 changed files with 164 additions and 57 deletions

View File

@@ -54,6 +54,51 @@ Layer::~Layer() {
deleteTexture(); deleteTexture();
} }
uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
return uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
}
uint32_t Layer::computeIdealHeight(uint32_t layerHeight) {
return uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE);
}
bool Layer::resize(const uint32_t width, const uint32_t height) {
uint32_t desiredWidth = computeIdealWidth(width);
uint32_t desiredHeight = computeIdealWidth(height);
if (desiredWidth <= getWidth() && desiredHeight <= getHeight()) {
return true;
}
uint32_t oldWidth = getWidth();
uint32_t oldHeight = getHeight();
setSize(desiredWidth, desiredHeight);
if (fbo) {
Caches::getInstance().activeTexture(0);
bindTexture();
allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
if (glGetError() != GL_NO_ERROR) {
setSize(oldWidth, oldHeight);
return false;
}
}
if (stencil) {
bindStencilRenderBuffer();
allocateStencilRenderBuffer();
if (glGetError() != GL_NO_ERROR) {
setSize(oldWidth, oldHeight);
return false;
}
}
return true;
}
void Layer::removeFbo(bool flush) { void Layer::removeFbo(bool flush) {
if (stencil) { if (stencil) {
// TODO: recycle & cache instead of simply deleting // TODO: recycle & cache instead of simply deleting

View File

@@ -48,6 +48,9 @@ struct Layer {
Layer(const uint32_t layerWidth, const uint32_t layerHeight); Layer(const uint32_t layerWidth, const uint32_t layerHeight);
~Layer(); ~Layer();
static uint32_t computeIdealWidth(uint32_t layerWidth);
static uint32_t computeIdealHeight(uint32_t layerHeight);
/** /**
* Calling this method will remove (either by recycling or * Calling this method will remove (either by recycling or
* destroying) the associated FBO, if present, and any render * destroying) the associated FBO, if present, and any render
@@ -91,6 +94,17 @@ struct Layer {
return texture.height; return texture.height;
} }
/**
* Resize the layer and its texture if needed.
*
* @param width The new width of the layer
* @param height The new height of the layer
*
* @return True if the layer was resized or nothing happened, false if
* a failure occurred during the resizing operation
*/
bool resize(const uint32_t width, const uint32_t height);
void setSize(uint32_t width, uint32_t height) { void setSize(uint32_t width, uint32_t height) {
texture.width = width; texture.width = width;
texture.height = height; texture.height = height;
@@ -203,6 +217,12 @@ struct Layer {
} }
} }
inline void bindStencilRenderBuffer() {
if (stencil) {
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
}
}
inline void generateTexture() { inline void generateTexture() {
if (!texture.id) { if (!texture.id) {
glGenTextures(1, &texture.id); glGenTextures(1, &texture.id);
@@ -229,7 +249,16 @@ struct Layer {
#if DEBUG_LAYERS #if DEBUG_LAYERS
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight()); ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
#endif #endif
glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL); if (texture.id) {
glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0,
format, storage, NULL);
}
}
inline void allocateStencilRenderBuffer() {
if (stencil) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, getWidth(), getHeight());
}
} }
inline mat4& getTexTransform() { inline mat4& getTexTransform() {

View File

@@ -136,31 +136,6 @@ void LayerCache::dump() {
} }
} }
bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) {
// TODO: We should be smarter and see if we have a texture of the appropriate
// size already in the cache, and reuse it instead of creating a new one
LayerEntry entry(width, height);
if (entry.mWidth <= layer->getWidth() && entry.mHeight <= layer->getHeight()) {
return true;
}
uint32_t oldWidth = layer->getWidth();
uint32_t oldHeight = layer->getHeight();
Caches::getInstance().activeTexture(0);
layer->bindTexture();
layer->setSize(entry.mWidth, entry.mHeight);
layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
if (glGetError() != GL_NO_ERROR) {
layer->setSize(oldWidth, oldHeight);
return false;
}
return true;
}
bool LayerCache::put(Layer* layer) { bool LayerCache::put(Layer* layer) {
if (!layer->isCacheable()) return false; if (!layer->isCacheable()) return false;

View File

@@ -72,17 +72,6 @@ public:
* Clears the cache. This causes all layers to be deleted. * Clears the cache. This causes all layers to be deleted.
*/ */
void clear(); void clear();
/**
* Resize the specified layer if needed.
*
* @param layer The layer to resize
* @param width The new width of the layer
* @param height The new height of the layer
*
* @return True if the layer was resized or nothing happened, false if
* a failure occurred during the resizing operation
*/
bool resize(Layer* layer, const uint32_t width, const uint32_t height);
/** /**
* Sets the maximum size of the cache in bytes. * Sets the maximum size of the cache in bytes.
@@ -108,8 +97,8 @@ public:
} }
LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) { LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) {
mWidth = uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE); mWidth = Layer::computeIdealWidth(layerWidth);
mHeight = uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE); mHeight = Layer::computeIdealHeight(layerHeight);
} }
LayerEntry(Layer* layer): LayerEntry(Layer* layer):

View File

@@ -264,7 +264,7 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
if (layer) { if (layer) {
LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height); LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height);
if (Caches::getInstance().layerCache.resize(layer, width, height)) { if (layer->resize(width, height)) {
layer->layer.set(0.0f, 0.0f, width, height); layer->layer.set(0.0f, 0.0f, width, height);
layer->texCoords.set(0.0f, height / float(layer->getHeight()), layer->texCoords.set(0.0f, height / float(layer->getHeight()),
width / float(layer->getWidth()), 0.0f); width / float(layer->getWidth()), 0.0f);

View File

@@ -1248,11 +1248,12 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
// TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached // TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
GLuint buffer; GLuint buffer;
glGenRenderbuffers(1, &buffer); glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
layer->getWidth(), layer->getHeight());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
layer->setStencilRenderBuffer(buffer); layer->setStencilRenderBuffer(buffer);
layer->bindStencilRenderBuffer();
layer->allocateStencilRenderBuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
} }
} }

View File

@@ -48,13 +48,32 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Grow layer" /> android:text="Grow layer" />
<Button
android:onClick="enableClip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Circle clip" />
<Button
android:onClick="disableClip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No clip" />
</LinearLayout> </LinearLayout>
<ListView <view class="com.android.test.hwui.ViewLayersActivity5$ClipFrameLayout"
android:id="@+id/list1" android:id="@+id/container"
android:layout_width="0dip" android:layout_width="0dip"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" /> android:layout_weight="1">
<ListView
android:id="@+id/list1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</view>
</LinearLayout> </LinearLayout>

View File

@@ -19,14 +19,18 @@ package com.android.test.hwui;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
import android.os.Bundle; import android.os.Bundle;
import android.util.AttributeSet;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
@@ -37,30 +41,75 @@ public class ViewLayersActivity5 extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
init();
setContentView(R.layout.view_layers_5); setContentView(R.layout.view_layers_5);
mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY));
setupList(R.id.list1); setupList(R.id.list1);
} }
public static class ClipFrameLayout extends FrameLayout {
private final Path mClipPath = new Path();
private boolean mClipEnabled;
public ClipFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ClipFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public boolean isClipEnabled() {
return mClipEnabled;
}
public void setClipEnabled(boolean clipEnabled) {
mClipEnabled = clipEnabled;
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (mClipEnabled) {
mClipPath.reset();
mClipPath.addCircle(getWidth() / 2.0f, getHeight() / 2.0f,
Math.min(getWidth(), getHeight()) / 3.0f, Path.Direction.CW);
canvas.clipPath(mClipPath);
}
super.dispatchDraw(canvas);
}
}
private void init() {
mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY));
}
public void enableClip(View v) {
((ClipFrameLayout) findViewById(R.id.container)).setClipEnabled(true);
}
public void disableClip(View v) {
((ClipFrameLayout) findViewById(R.id.container)).setClipEnabled(false);
}
public void enableLayer(View v) { public void enableLayer(View v) {
findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); findViewById(R.id.container).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
} }
public void disableLayer(View v) { public void disableLayer(View v) {
findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_NONE, null); findViewById(R.id.container).setLayerType(View.LAYER_TYPE_NONE, null);
} }
public void growLayer(View v) { public void growLayer(View v) {
findViewById(R.id.list1).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; findViewById(R.id.container).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
findViewById(R.id.list1).requestLayout(); findViewById(R.id.container).requestLayout();
} }
public void shrinkLayer(View v) { public void shrinkLayer(View v) {
findViewById(R.id.list1).getLayoutParams().height = 300; findViewById(R.id.container).getLayoutParams().height = 300;
findViewById(R.id.list1).requestLayout(); findViewById(R.id.container).requestLayout();
} }
private void setupList(int listId) { private void setupList(int listId) {