Add region-tracking to OffscreenBuffers
Change-Id: I024c7219c080b9a89888517f5a89d49dfe8065ba
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 7055839..2fca5ea 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -20,6 +20,7 @@
#include "Glop.h"
#include "GlopBuilder.h"
#include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
#include "utils/GLUtils.h"
namespace android {
@@ -29,12 +30,13 @@
// OffscreenBuffer
////////////////////////////////////////////////////////////////////////////////
-OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t textureWidth, uint32_t textureHeight,
uint32_t viewportWidth, uint32_t viewportHeight)
- : viewportWidth(viewportWidth)
+ : renderState(renderState)
+ , viewportWidth(viewportWidth)
, viewportHeight(viewportHeight)
- , texture(caches)
- , texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
+ , texture(caches) {
texture.width = textureWidth;
texture.height = textureHeight;
@@ -50,19 +52,58 @@
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
+void OffscreenBuffer::updateMeshFromRegion() {
+ // avoid T-junctions as they cause artifacts in between the resultant
+ // geometry when complex transforms occur.
+ // TODO: generate the safeRegion only if necessary based on drawing transform
+ Region safeRegion = Region::createTJunctionFreeRegion(region);
+
+ size_t count;
+ const android::Rect* rects = safeRegion.getArray(&count);
+
+ const float texX = 1.0f / float(viewportWidth);
+ const float texY = 1.0f / float(viewportHeight);
+
+ FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
+ TextureVertex* mesh = &meshVector[0];
+ for (size_t i = 0; i < count; i++) {
+ const android::Rect* r = &rects[i];
+
+ const float u1 = r->left * texX;
+ const float v1 = (viewportHeight - r->top) * texY;
+ const float u2 = r->right * texX;
+ const float v2 = (viewportHeight - 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);
+ }
+ elementCount = count * 6;
+ renderState.meshState().genOrUpdateMeshBuffer(&vbo,
+ sizeof(TextureVertex) * count * 4,
+ &meshVector[0],
+ GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
+}
+
+OffscreenBuffer::~OffscreenBuffer() {
+ texture.deleteTexture();
+ renderState.meshState().deleteMeshBuffer(vbo);
+ elementCount = 0;
+ vbo = 0;
+}
+
////////////////////////////////////////////////////////////////////////////////
// BakedOpRenderer
////////////////////////////////////////////////////////////////////////////////
-OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(uint32_t width, uint32_t height) {
+OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(RenderState& renderState,
+ uint32_t width, uint32_t height) {
// TODO: get from cache!
- return new OffscreenBuffer(Caches::getInstance(), width, height, width, height);
+ return new OffscreenBuffer(renderState, Caches::getInstance(), width, height, width, height);
}
void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
- // destroy and delete, since each clipped saveLayer is only drawn once.
- offscreenBuffer->texture.deleteTexture();
-
// TODO: return texture/offscreenbuffer to cache!
delete offscreenBuffer;
}
@@ -70,7 +111,7 @@
OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
- OffscreenBuffer* buffer = createOffscreenBuffer(width, height);
+ OffscreenBuffer* buffer = createOffscreenBuffer(mRenderState, width, height);
startLayer(buffer);
return buffer;
}
@@ -98,6 +139,7 @@
}
void BakedOpRenderer::endLayer() {
+ mRenderTarget.offscreenBuffer->updateMeshFromRegion();
mRenderTarget.offscreenBuffer = nullptr;
// Detach the texture from the FBO
@@ -162,6 +204,12 @@
mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
clip.getWidth(), clip.getHeight());
}
+ if (mRenderTarget.offscreenBuffer) { // TODO: not with multi-draw
+ // register layer damage to draw-back region
+ const Rect& uiDirty = state.computedState.clippedBounds;
+ android::Rect dirty(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
+ mRenderTarget.offscreenBuffer->region.orSelf(dirty);
+ }
mRenderState.render(glop, mRenderTarget.orthoMatrix);
mHasDrawn = true;
}
@@ -174,6 +222,14 @@
LOG_ALWAYS_FATAL("unsupported operation");
}
+void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
Texture* texture = renderer.getTexture(op.bitmap);
@@ -217,28 +273,20 @@
renderer.renderGlop(state, glop);
}
-void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
- LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
- LOG_ALWAYS_FATAL("unsupported operation");
-}
-
void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
OffscreenBuffer* buffer = *op.layerHandle;
// TODO: extend this to handle HW layers & paint properties which
// reside in node.properties().layerProperties()
float layerAlpha = op.alpha * state.alpha;
- const bool tryToSnap = state.computedState.transform.isPureTranslate();
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, buffer->texCoords)
+ .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
.setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
.setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+ .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+ Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
.build();
renderer.renderGlop(state, glop);
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 722bf02..aa1e67d 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -35,13 +35,24 @@
*/
class OffscreenBuffer {
public:
- OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+ OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t textureWidth, uint32_t textureHeight,
uint32_t viewportWidth, uint32_t viewportHeight);
+ ~OffscreenBuffer();
+
+ // must be called prior to rendering, to construct/update vertex buffer
+ void updateMeshFromRegion();
+
+ RenderState& renderState;
uint32_t viewportWidth;
uint32_t viewportHeight;
Texture texture;
- Rect texCoords;
+
+ // Portion of offscreen buffer that has been drawn to. Used to minimize drawing area when
+ // drawing back to screen / parent FBO.
Region region;
+ GLsizei elementCount = 0;
+ GLuint vbo = 0;
};
/**
@@ -61,7 +72,8 @@
, mOpaque(opaque) {
}
- static OffscreenBuffer* createOffscreenBuffer(uint32_t width, uint32_t height);
+ static OffscreenBuffer* createOffscreenBuffer(RenderState& renderState,
+ uint32_t width, uint32_t height);
static void destroyOffscreenBuffer(OffscreenBuffer*);
RenderState& renderState() { return mRenderState; }
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index d2da851..f3ac93b 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -70,6 +70,20 @@
// Mesh
////////////////////////////////////////////////////////////////////////////////
+GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+ mOutGlop->mesh.vertices = {
+ vbo,
+ VertexAttribFlags::TextureCoord,
+ nullptr, nullptr, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = elementCount;
+ return *this;
+}
+
GlopBuilder& GlopBuilder::setMeshUnitQuad() {
TRIGGER_STAGE(kMeshStage);
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 6f5802e..6270dcb 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -47,6 +47,7 @@
public:
GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
+ GlopBuilder& setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount);
GlopBuilder& setMeshUnitQuad();
GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index de02bb8..0601944 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -250,7 +250,7 @@
layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
#if HWUI_NEW_OPS
- return BakedOpRenderer::createOffscreenBuffer(width, height);
+ return BakedOpRenderer::createOffscreenBuffer(renderState, width, height);
#else
return LayerRenderer::createRenderLayer(renderState, width, height);
#endif
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index 0521f65..03cb5ce 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -100,6 +100,24 @@
return false;
}
+void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size,
+ const void* data, GLenum usage) {
+ if (!*buffer) {
+ glGenBuffers(1, buffer);
+ }
+ bindMeshBuffer(*buffer);
+ glBufferData(GL_ARRAY_BUFFER, size, data, usage);
+}
+
+void MeshState::deleteMeshBuffer(GLuint buffer) {
+ if (buffer == mCurrentBuffer) {
+ // GL defines that deleting the currently bound VBO rebinds to 0 (no VBO).
+ // Reflect this in our cached value.
+ mCurrentBuffer = 0;
+ }
+ glDeleteBuffers(1, &buffer);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Vertices
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index e80f4d0..6c0fb78 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -75,6 +75,9 @@
*/
bool unbindMeshBuffer();
+ void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage);
+ void deleteMeshBuffer(GLuint);
+
///////////////////////////////////////////////////////////////////////////////
// Vertices
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
index fb760ac5..1f1f50a 100644
--- a/libs/hwui/unit_tests/FatVectorTests.cpp
+++ b/libs/hwui/unit_tests/FatVectorTests.cpp
@@ -56,6 +56,27 @@
}
}
+TEST(FatVector, preSizeConstructor) {
+ {
+ FatVector<int, 4> v(32);
+ EXPECT_EQ(32u, v.capacity());
+ EXPECT_EQ(32u, v.size());
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+ {
+ FatVector<int, 4> v(4);
+ EXPECT_EQ(4u, v.capacity());
+ EXPECT_EQ(4u, v.size());
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+ {
+ FatVector<int, 4> v(2);
+ EXPECT_EQ(4u, v.capacity());
+ EXPECT_EQ(2u, v.size());
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+}
+
TEST(FatVector, shrink) {
FatVector<int, 10> v;
EXPECT_TRUE(allocationIsInternal(v));
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index c3c16c5a..315c249 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -91,6 +91,10 @@
InlineStdAllocator<T, SIZE>(mAllocation)) {
this->reserve(SIZE);
}
+
+ FatVector(size_t capacity) : FatVector() {
+ this->resize(capacity);
+ }
private:
typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
};