diff options
-rw-r--r-- | libs/hwui/DisplayListOp.h | 115 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 55 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 6 | ||||
-rw-r--r-- | libs/hwui/Patch.cpp | 26 | ||||
-rw-r--r-- | libs/hwui/Patch.h | 7 | ||||
-rw-r--r-- | libs/hwui/PatchCache.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/PixelBuffer.cpp | 5 |
8 files changed, 171 insertions, 55 deletions
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index f678bfd..6c6eabb 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -751,12 +751,18 @@ public: TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \ texCoordsRect.xDim, texCoordsRect.yDim) + /** + * This multi-draw operation builds a mesh on the stack by generating a quad + * for each bitmap in the batch. This method is also responsible for dirtying + * the current layer, if any. + */ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, const Vector<DrawOp*>& ops, const Rect& bounds) { renderer.restoreDisplayState(state, true); // restore all but the clip TextureVertex vertices[6 * ops.size()]; TextureVertex* vertex = &vertices[0]; + const bool hasLayer = renderer.hasLayer(); bool transformed = false; // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, @@ -778,6 +784,11 @@ public: SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom); SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top); SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom); + + if (hasLayer) { + const Rect& dirty = ops[i]->state.mBounds; + renderer.dirtyLayer(dirty.left, dirty.top, dirty.right, dirty.bottom); + } } return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], @@ -921,25 +932,104 @@ private: class DrawPatchOp : public DrawBoundedOp { public: DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) - : DrawBoundedOp(left, top, right, bottom, 0), - mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode), - mGenerationId(0), mMesh(NULL) { + float left, float top, float right, float bottom, SkPaint* paint) + : DrawBoundedOp(left, top, right, bottom, paint), + mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL) { mEntry = Caches::getInstance().assetAtlas.getEntry(bitmap); }; - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + const Patch* getMesh(OpenGLRenderer& renderer) { if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) { PatchCache& cache = renderer.getCaches().patchCache; mMesh = cache.get(mEntry, mBitmap->width(), mBitmap->height(), - mLocalBounds.right - mLocalBounds.left, mLocalBounds.bottom - mLocalBounds.top, - mPatch); + mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch); mGenerationId = cache.getGenerationId(); } + return mMesh; + } + + /** + * This multi-draw operation builds an indexed mesh on the stack by copying + * and transforming the vertices of each 9-patch in the batch. This method + * is also responsible for dirtying the current layer, if any. + */ + virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, + const Vector<DrawOp*>& ops, const Rect& bounds) { + renderer.restoreDisplayState(state, true); + + // Batches will usually contain a small number of items so it's + // worth performing a first iteration to count the exact number + // of vertices we need in the new mesh + uint32_t totalVertices = 0; + for (unsigned int i = 0; i < ops.size(); i++) { + totalVertices += ((DrawPatchOp*) ops[i])->getMesh(renderer)->verticesCount; + } + + const bool hasLayer = renderer.hasLayer(); + + uint32_t indexCount = 0; + + TextureVertex vertices[totalVertices]; + TextureVertex* vertex = &vertices[0]; + + // Create a mesh that contains the transformed vertices for all the + // 9-patch objects that are part of the batch. Note that onDefer() + // enforces ops drawn by this function to have a pure translate or + // identity matrix + for (unsigned int i = 0; i < ops.size(); i++) { + DrawPatchOp* patchOp = (DrawPatchOp*) ops[i]; + const Patch* opMesh = patchOp->getMesh(renderer); + uint32_t vertexCount = opMesh->verticesCount; + if (vertexCount == 0) continue; + + // We use the bounds to know where to translate our vertices + // Using patchOp->state.mBounds wouldn't work because these + // bounds are clipped + const float tx = (int) floorf(patchOp->state.mMatrix.getTranslateX() + + patchOp->mLocalBounds.left + 0.5f); + const float ty = (int) floorf(patchOp->state.mMatrix.getTranslateY() + + patchOp->mLocalBounds.top + 0.5f); + + // Copy & transform all the vertices for the current operation + TextureVertex* opVertices = opMesh->vertices; + for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { + TextureVertex::set(vertex++, + opVertices->position[0] + tx, opVertices->position[1] + ty, + opVertices->texture[0], opVertices->texture[1]); + } + + // Dirty the current layer if possible. When the 9-patch does not + // contain empty quads we can take a shortcut and simply set the + // dirty rect to the object's bounds. + if (hasLayer) { + if (!opMesh->hasEmptyQuads) { + renderer.dirtyLayer(tx, ty, + tx + patchOp->mLocalBounds.getWidth(), + ty + patchOp->mLocalBounds.getHeight()); + } else { + const size_t count = opMesh->quads.size(); + for (size_t i = 0; i < count; i++) { + const Rect& quadBounds = opMesh->quads[i]; + const float x = tx + quadBounds.left; + const float y = ty + quadBounds.top; + renderer.dirtyLayer(x, y, + x + quadBounds.getWidth(), y + quadBounds.getHeight()); + } + } + } + + indexCount += opMesh->indexCount; + } + + return renderer.drawPatches(mBitmap, mEntry, &vertices[0], indexCount, getPaint(renderer)); + } + + virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { // We're not calling the public variant of drawPatch() here // This method won't perform the quickReject() since we've already done it at this point - return renderer.drawPatch(mBitmap, mMesh, mEntry, mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode); + return renderer.drawPatch(mBitmap, getMesh(renderer), mEntry, + mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, + getPaint(renderer)); } virtual void output(int level, uint32_t logFlags) { @@ -951,7 +1041,8 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch; deferInfo.mergeId = mEntry ? (mergeid_t) &mEntry->atlas : (mergeid_t) mBitmap; - deferInfo.mergeable = true; + deferInfo.mergeable = state.mMatrix.isPureTranslate() && + OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; deferInfo.opaqueOverBounds = isOpaqueOverBounds() && mBitmap->isOpaque(); } @@ -959,11 +1050,9 @@ private: SkBitmap* mBitmap; Res_png_9patch* mPatch; - int mAlpha; - SkXfermode::Mode mMode; - uint32_t mGenerationId; const Patch* mMesh; + AssetAtlas::Entry* mEntry; }; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 6d85a16..41b1507 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -317,13 +317,9 @@ status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, in status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint) { - int alpha; - SkXfermode::Mode mode; - OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); - bitmap = refBitmap(bitmap); - addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, alpha, mode)); + addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint)); return DrawGlInfo::kStatusDone; } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 1dcf21a..d6d7b7f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1191,7 +1191,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { // after we setup drawing in case we need to mess with the // stencil buffer in setupDraw() TextureVertex* mesh = mCaches.getRegionMesh(); - GLsizei numQuads = 0; + uint32_t numQuads = 0; setupDrawWithTexture(); setupDrawColor(alpha, alpha, alpha, alpha); @@ -2049,6 +2049,11 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); } +/** + * Important note: this method is intended to draw batches of bitmaps and + * will not set the scissor enable or dirty the current layer, if any. + * The caller is responsible for properly dirtying the current layer. + */ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices, bool transformed, const Rect& bounds, SkPaint* paint) { mCaches.activeTexture(0); @@ -2071,12 +2076,12 @@ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureV drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), texture->id, paint != NULL, color, alpha, mode, &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, bitmapCount * 6, true, true); + GL_TRIANGLES, bitmapCount * 6, true, true, false); } else { drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), texture->id, alpha / 255.0f, mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, bitmapCount * 6, false, true, 0, true); + GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false); } return DrawGlInfo::kStatusDrew; @@ -2367,10 +2372,6 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint) { - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - if (quickReject(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2379,13 +2380,11 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(), right - left, bottom - top, patch); - return drawPatch(bitmap, mesh, entry, left, top, right, bottom, alpha, mode); + return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint); } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, - AssetAtlas::Entry* entry, float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode) { - +status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, + float left, float top, float right, float bottom, SkPaint* paint) { if (quickReject(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2399,6 +2398,10 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, texture->setWrap(GL_CLAMP_TO_EDGE, true); texture->setFilter(GL_LINEAR, true); + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + const bool pureTranslate = currentTransform().isPureTranslate(); // Mark the current layer dirty where we are going to draw the patch if (hasLayer() && mesh->hasEmptyQuads) { @@ -2418,8 +2421,6 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, } } - alpha *= mSnapshot->alpha; - if (CC_LIKELY(pureTranslate)) { const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); @@ -2441,6 +2442,32 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, return DrawGlInfo::kStatusDrew; } +/** + * Important note: this method is intended to draw batches of 9-patch objects and + * will not set the scissor enable or dirty the current layer, if any. + * The caller is responsible for properly dirtying the current layer. + */ +status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, + TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) { + mCaches.activeTexture(0); + Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); + if (!texture) return DrawGlInfo::kStatusDone; + const AutoTexture autoCleanup(texture); + + texture->setWrap(GL_CLAMP_TO_EDGE, true); + texture->setFilter(GL_LINEAR, true); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f, + mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0], + GL_TRIANGLES, indexCount, false, true, 0, true, false); + + return DrawGlInfo::kStatusDrew; +} + status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint, bool useOffset) { if (!vertexBuffer.getVertexCount()) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 0760f9c..548501d 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -297,10 +297,12 @@ public: virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint); virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, float* vertices, int* colors, SkPaint* paint); + status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, + TextureVertex* vertices, uint32_t indexCount, SkPaint* paint); virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint); status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode); + float left, float top, float right, float bottom, SkPaint* paint); virtual status_t drawColor(int color, SkXfermode::Mode mode); virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint); virtual status_t drawRoundRect(float left, float top, float right, float bottom, @@ -1115,6 +1117,8 @@ private: friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; + friend class DrawBitmapOp; + friend class DrawPatchOp; }; // class OpenGLRenderer diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 6b0734a..9e3e701 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -32,7 +32,7 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -Patch::Patch(): verticesCount(0), indexCount(0), hasEmptyQuads(false) { +Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(false) { } Patch::~Patch() { @@ -47,14 +47,14 @@ uint32_t Patch::getSize() const { } TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight, - float left, float top, float right, float bottom, const Res_png_9patch* patch) { + float width, float height, const Res_png_9patch* patch) { UvMapper mapper; - return createMesh(bitmapWidth, bitmapHeight, left, top, right, bottom, mapper, patch); + return createMesh(bitmapWidth, bitmapHeight, width, height, mapper, patch); } TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight, - float left, float top, float right, float bottom, - const UvMapper& mapper, const Res_png_9patch* patch) { + float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) { + if (vertices) return vertices; const uint32_t* colors = &patch->colors[0]; const int8_t numColors = patch->numColors; @@ -79,7 +79,7 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4; if (maxVertices == 0) return NULL; - TextureVertex* vertices = new TextureVertex[maxVertices]; + vertices = new TextureVertex[maxVertices]; TextureVertex* vertex = vertices; const int32_t* xDivs = patch->xDivs; @@ -101,9 +101,9 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig } const float xStretchTex = stretchSize; const float fixed = bitmapWidth - stretchSize; - const float xStretch = fmaxf(right - left - fixed, 0.0f); + const float xStretch = fmaxf(width - fixed, 0.0f); stretchX = xStretch / xStretchTex; - rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(right - left, 0.0f) / fixed, 1.0f); + rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(width, 0.0f) / fixed, 1.0f); } if (yStretchCount > 0) { @@ -113,9 +113,9 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig } const float yStretchTex = stretchSize; const float fixed = bitmapHeight - stretchSize; - const float yStretch = fmaxf(bottom - top - fixed, 0.0f); + const float yStretch = fmaxf(height - fixed, 0.0f); stretchY = yStretch / yStretchTex; - rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f); + rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(height, 0.0f) / fixed, 1.0f); } uint32_t quadCount = 0; @@ -144,7 +144,7 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig if (stepY > 0.0f) { generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX, - right - left, bitmapWidth, quadCount); + width, bitmapWidth, quadCount); } y1 = y2; @@ -154,9 +154,9 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig } if (previousStepY != bitmapHeight) { - y2 = bottom - top; + y2 = height; generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, - right - left, bitmapWidth, quadCount); + width, bitmapWidth, quadCount); } return vertices; diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index 448cf60..246ba66 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -45,6 +45,7 @@ struct Patch { */ uint32_t getSize() const; + TextureVertex* vertices; uint32_t verticesCount; uint32_t indexCount; bool hasEmptyQuads; @@ -54,11 +55,9 @@ struct Patch { GLintptr textureOffset; TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight, - float left, float top, float right, float bottom, - const Res_png_9patch* patch); + float width, float height, const Res_png_9patch* patch); TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight, - float left, float top, float right, float bottom, - const UvMapper& mapper, const Res_png_9patch* patch); + float width, float height, const UvMapper& mapper, const Res_png_9patch* patch); private: void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index c23e991..dc69d7f 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -118,10 +118,10 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry, if (entry) { vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, - 0.0f, 0.0f, pixelWidth, pixelHeight, entry->uvMapper, patch); + pixelWidth, pixelHeight, entry->uvMapper, patch); } else { vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, - 0.0f, 0.0f, pixelWidth, pixelHeight, patch); + pixelWidth, pixelHeight, patch); } if (vertices) { @@ -141,8 +141,6 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry, mSize += size; glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); - - delete[] vertices; } mCache.put(description, newMesh); diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp index 74b628a..36e89c6 100644 --- a/libs/hwui/PixelBuffer.cpp +++ b/libs/hwui/PixelBuffer.cpp @@ -132,7 +132,10 @@ void GpuPixelBuffer::unmap() { if (mAccessMode != kAccessMode_None) { if (mMappedPointer) { mCaches.bindPixelBuffer(mBuffer); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + if (status == GL_FALSE) { + ALOGE("Corrupted GPU pixel buffer"); + } } mAccessMode = kAccessMode_None; mMappedPointer = NULL; |