Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7964751 authored by Chris Craik's avatar Chris Craik Committed by Android (Google) Code Review
Browse files

Merge "Add bitmapmesh, rect, and patch rendering"

parents 7aa8aef5 f09ff5aa
Loading
Loading
Loading
Loading
+234 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "Caches.h"
#include "Glop.h"
#include "GlopBuilder.h"
#include "Patch.h"
#include "PathTessellator.h"
#include "renderstate/OffscreenBufferPool.h"
#include "renderstate/RenderState.h"
@@ -56,8 +57,6 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
    if (entry) {
        entry->uvMapper.map(texCoords);
    }
    // init to non-empty, so we can safely expandtoCoverRect
    Rect totalBounds = firstState.computedState.clippedBounds;
    for (size_t i = 0; i < opList.count; i++) {
        const BakedOpState& state = *(opList.states[i]);
        TextureVertex* rectVerts = &vertices[i * 4];
@@ -68,8 +67,6 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
        }
        storeTexturedRect(rectVerts, opBounds, texCoords);
        renderer.dirtyRenderTarget(opBounds);

        totalBounds.expandToCover(opBounds);
    }

    const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
@@ -80,7 +77,111 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
            .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
            .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
            .setTransform(Matrix4::identity(), TransformFlags::None)
            .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above
            .setModelViewIdentityEmptyBounds()
            .build();
    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
}

void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
        const MergedBakedOpList& opList) {
    const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
    const BakedOpState& firstState = *(opList.states[0]);
    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
            firstOp.bitmap->pixelRef());

    // 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 (size_t i = 0; i < opList.count; i++) {
        const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));

        // TODO: cache mesh lookups
        const Patch* opMesh = renderer.caches().patchCache.get(
                entry, op.bitmap->width(), op.bitmap->height(),
                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
        totalVertices += opMesh->verticesCount;
    }

    const bool dirtyRenderTarget = renderer.offscreenRenderTarget();

    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 (size_t i = 0; i < opList.count; i++) {
        const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
        const BakedOpState& state = *opList.states[i];

        // TODO: cache mesh lookups
        const Patch* opMesh = renderer.caches().patchCache.get(
                entry, op.bitmap->width(), op.bitmap->height(),
                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);


        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 = floorf(state.computedState.transform.getTranslateX()
                + op.unmappedBounds.left + 0.5f);
        const float ty = floorf(state.computedState.transform.getTranslateY()
                + op.unmappedBounds.top + 0.5f);

        // Copy & transform all the vertices for the current operation
        TextureVertex* opVertices = opMesh->vertices.get();
        for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
            TextureVertex::set(vertex++,
                    opVertices->x + tx, opVertices->y + ty,
                    opVertices->u, opVertices->v);
        }

        // 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 (dirtyRenderTarget) {
            if (!opMesh->hasEmptyQuads) {
                renderer.dirtyRenderTarget(Rect(tx, ty,
                        tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.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.dirtyRenderTarget(Rect(x, y,
                            x + quadBounds.getWidth(), y + quadBounds.getHeight()));
                }
            }
        }

        indexCount += opMesh->indexCount;
    }


    Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
    if (!texture) return;
    const AutoTexture autoCleanup(texture);

    // 9 patches are built for stretching - always filter
    int textureFillFlags = TextureFillFlags::ForceFilter;
    if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
    }
    Glop glop;
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(firstState.roundRectClipState)
            .setMeshTexturedIndexedQuads(vertices, indexCount)
            .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
            .setTransform(Matrix4::identity(), TransformFlags::None)
            .setModelViewIdentityEmptyBounds()
            .build();
    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
}
@@ -310,6 +411,105 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op
    renderer.renderGlop(state, glop);
}

void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
    const static UvMapper defaultUvMapper;
    const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;

    std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
    ColorTextureVertex* vertex = &mesh[0];

    const int* colors = op.colors;
    std::unique_ptr<int[]> tempColors;
    if (!colors) {
        uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
        tempColors.reset(new int[colorsCount]);
        memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
        colors = tempColors.get();
    }

    Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
    const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);

    for (int32_t y = 0; y < op.meshHeight; y++) {
        for (int32_t x = 0; x < op.meshWidth; x++) {
            uint32_t i = (y * (op.meshWidth + 1) + x) * 2;

            float u1 = float(x) / op.meshWidth;
            float u2 = float(x + 1) / op.meshWidth;
            float v1 = float(y) / op.meshHeight;
            float v2 = float(y + 1) / op.meshHeight;

            mapper.map(u1, v1, u2, v2);

            int ax = i + (op.meshWidth + 1) * 2;
            int ay = ax + 1;
            int bx = i;
            int by = bx + 1;
            int cx = i + 2;
            int cy = cx + 1;
            int dx = i + (op.meshWidth + 1) * 2 + 2;
            int dy = dx + 1;

            const float* vertices = op.vertices;
            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
            ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);

            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
            ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
        }
    }

    if (!texture) {
        texture = renderer.caches().textureCache.get(op.bitmap);
        if (!texture) {
            return;
        }
    }
    const AutoTexture autoCleanup(texture);

    /*
     * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
     * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
     */
    const int textureFillFlags = TextureFillFlags::None;
    Glop glop;
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshColoredTexturedMesh(mesh.get(), elementCount)
            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
            .setTransform(state.computedState.transform,  TransformFlags::None)
            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
            .build();
    renderer.renderGlop(state, glop);
}

void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
    Texture* texture = renderer.getTexture(op.bitmap);
    if (!texture) return;
    const AutoTexture autoCleanup(texture);

    Rect uv(std::max(0.0f, op.src.left / texture->width),
            std::max(0.0f, op.src.top / texture->height),
            std::min(1.0f, op.src.right / texture->width),
            std::min(1.0f, op.src.bottom / texture->height));

    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
    const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
            && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
    Glop glop;
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshTexturedUvQuad(texture->uvMapper, uv)
            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
            .build();
    renderer.renderGlop(state, glop);
}

void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
    VertexBuffer buffer;
    PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
@@ -334,6 +534,35 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co
    }
}

void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
    // 9 patches are built for stretching - always filter
    int textureFillFlags = TextureFillFlags::ForceFilter;
    if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
    }

    // TODO: avoid redoing the below work each frame:
    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
    const Patch* mesh = renderer.caches().patchCache.get(
            entry, op.bitmap->width(), op.bitmap->height(),
            op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);

    Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
    if (!texture) return;
    const AutoTexture autoCleanup(texture);
    Glop glop;
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshPatchQuads(*mesh)
            .setMeshTexturedUnitQuad(texture->uvMapper)
            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
            .build();
    renderer.renderGlop(state, glop);
}

void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
    PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
    const AutoTexture holder(texture);
+1 −1
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
            .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
            .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
            .setTransform(bakedState->computedState.transform, transformFlags)
            .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
            .setModelViewIdentityEmptyBounds()
            .build();
    // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
    renderer->renderGlop(nullptr, clip, glop);
+1 −1
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ namespace TransformFlags {

        // Canvas transform isn't applied to the mesh at draw time,
        //since it's already built in.
        MeshIgnoresCanvasTransform = 1 << 1,
        MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove
    };
};

+4 −0
Original line number Diff line number Diff line
@@ -95,6 +95,10 @@ public:
            return setModelViewOffsetRect(offsetX, offsetY, source);
        }
    }
    GlopBuilder& setModelViewIdentityEmptyBounds() {
        // pass empty rect since not needed for damage / snap
        return setModelViewOffsetRect(0, 0, Rect());
    }

    GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);

+29 −0
Original line number Diff line number Diff line
@@ -757,6 +757,18 @@ void OpReorderer::onBitmapOp(const BitmapOp& op) {
    }
}

void OpReorderer::onBitmapMeshOp(const BitmapMeshOp& op) {
    BakedOpState* bakedState = tryBakeOpState(op);
    if (!bakedState) return; // quick rejected
    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}

void OpReorderer::onBitmapRectOp(const BitmapRectOp& op) {
    BakedOpState* bakedState = tryBakeOpState(op);
    if (!bakedState) return; // quick rejected
    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}

void OpReorderer::onLinesOp(const LinesOp& op) {
    batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
    onStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
@@ -766,6 +778,23 @@ void OpReorderer::onOvalOp(const OvalOp& op) {
    onStrokeableOp(op, tessBatchId(op));
}

void OpReorderer::onPatchOp(const PatchOp& op) {
    BakedOpState* bakedState = tryBakeOpState(op);
    if (!bakedState) return; // quick rejected

    if (bakedState->computedState.transform.isPureTranslate()
            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
        // TODO: AssetAtlas in mergeId

        // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
        currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId);
    } else {
        // Use Bitmap batchId since Bitmap+Patch use same shader
        currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
    }
}

void OpReorderer::onPathOp(const PathOp& op) {
    onStrokeableOp(op, OpBatchType::Bitmap);
}
Loading