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

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

Merge "Rework receiver/dispatcher design slightly, and replace Layer usage."

parents ec721632 5854b348
Loading
Loading
Loading
Loading
+115 −95
Original line number Diff line number Diff line
@@ -25,190 +25,210 @@
namespace android {
namespace uirenderer {

void BakedOpRenderer::Info::setViewport(uint32_t width, uint32_t height) {
    viewportWidth = width;
    viewportHeight = height;
    orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
////////////////////////////////////////////////////////////////////////////////
// OffscreenBuffer
////////////////////////////////////////////////////////////////////////////////

    renderState.setViewport(width, height);
    renderState.blend().syncEnabled();
}
OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
        uint32_t viewportWidth, uint32_t viewportHeight)
        : texture(caches)
        , texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
    texture.width = textureWidth;
    texture.height = textureHeight;

Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
    Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
    if (!texture) {
        return caches.textureCache.get(bitmap);
    }
    return texture;
}
    caches.textureState().activateTexture(0);
    glGenTextures(1, &texture.id);
    caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);

void BakedOpRenderer::Info::renderGlop(const BakedOpState& state, const Glop& glop) {
    bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
    renderState.scissor().setEnabled(useScissor);
    if (useScissor) {
        const Rect& clip = state.computedState.clipRect;
        renderState.scissor().set(clip.left, viewportHeight - clip.bottom,
            clip.getWidth(), clip.getHeight());
    }
    renderState.render(glop, orthoMatrix);
    didDraw = true;
    texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
    // not setting filter on texture, since it's set when drawing, based on transform

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
            GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}

Layer* BakedOpRenderer::startLayer(Info& info, uint32_t width, uint32_t height) {
    info.caches.textureState().activateTexture(0);
    Layer* layer = info.caches.layerCache.get(info.renderState, width, height);
    LOG_ALWAYS_FATAL_IF(!layer, "need layer...");
////////////////////////////////////////////////////////////////////////////////
// BakedOpRenderer
////////////////////////////////////////////////////////////////////////////////

    info.layer = layer;
    layer->texCoords.set(0.0f, width / float(layer->getHeight()),
            height / float(layer->getWidth()), 0.0f);
OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
    LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");

    layer->setFbo(info.renderState.genFramebuffer());
    info.renderState.bindFramebuffer(layer->getFbo());
    layer->bindTexture();
    // TODO: really should be caching these!
    OffscreenBuffer* buffer = new OffscreenBuffer(mCaches, width, height, width, height);
    mRenderTarget.offscreenBuffer = buffer;

    // Initialize the texture if needed
    if (layer->isEmpty()) {
        layer->allocateTexture();
        layer->setEmpty(false);
    }
    // create and bind framebuffer
    mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
    mRenderState.bindFramebuffer(mRenderTarget.frameBufferId);

    // attach the texture to the FBO
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
            layer->getTextureId(), 0);
            buffer->texture.id, 0);
    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
    LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
            "framebuffer incomplete!");

    // Clear the FBO
    info.renderState.scissor().setEnabled(false);
    mRenderState.scissor().setEnabled(false);
    glClear(GL_COLOR_BUFFER_BIT);

    // Change the viewport & ortho projection
    info.setViewport(width, height);
    return layer;
    setViewport(width, height);
    return buffer;
}

void BakedOpRenderer::endLayer(Info& info) {
    Layer* layer = info.layer;
    info.layer = nullptr;
void BakedOpRenderer::endLayer() {
    mRenderTarget.offscreenBuffer = nullptr;

    // Detach the texture from the FBO
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
    layer->removeFbo(false);
    mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
    mRenderTarget.frameBufferId = -1;
}

void BakedOpRenderer::startFrame(Info& info, uint32_t width, uint32_t height) {
    info.renderState.bindFramebuffer(0);
    info.setViewport(width, height);
    Caches::getInstance().clearGarbage();
void BakedOpRenderer::startFrame(uint32_t width, uint32_t height) {
    mRenderState.bindFramebuffer(0);
    setViewport(width, height);
    mCaches.clearGarbage();

    if (!info.opaque) {
    if (!mOpaque) {
        // TODO: partial invalidate!
        info.renderState.scissor().setEnabled(false);
        mRenderState.scissor().setEnabled(false);
        glClear(GL_COLOR_BUFFER_BIT);
        info.didDraw = true;
        mHasDrawn = true;
    }
}
void BakedOpRenderer::endFrame(Info& info) {
    info.caches.pathCache.trim();
    info.caches.tessellationCache.trim();

void BakedOpRenderer::endFrame() {
    mCaches.pathCache.trim();
    mCaches.tessellationCache.trim();

#if DEBUG_OPENGL
    GLUtils::dumpGLErrors();
#endif

#if DEBUG_MEMORY_USAGE
    info.caches.dumpMemoryUsage();
    mCaches.dumpMemoryUsage();
#else
    if (Properties::debugLevel & kDebugMemory) {
        info.caches.dumpMemoryUsage();
        mCaches.dumpMemoryUsage();
    }
#endif
}

void BakedOpRenderer::onRenderNodeOp(Info&, const RenderNodeOp&, const BakedOpState&) {
void BakedOpRenderer::setViewport(uint32_t width, uint32_t height) {
    mRenderTarget.viewportWidth = width;
    mRenderTarget.viewportHeight = height;
    mRenderTarget.orthoMatrix.loadOrtho(width, height);

    mRenderState.setViewport(width, height);
    mRenderState.blend().syncEnabled();
}

Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
    Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
    if (!texture) {
        return mCaches.textureCache.get(bitmap);
    }
    return texture;
}

void BakedOpRenderer::renderGlop(const BakedOpState& state, const Glop& glop) {
    bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
    mRenderState.scissor().setEnabled(useScissor);
    if (useScissor) {
        const Rect& clip = state.computedState.clipRect;
        mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
            clip.getWidth(), clip.getHeight());
    }
    mRenderState.render(glop, mRenderTarget.orthoMatrix);
    mHasDrawn = true;
}

////////////////////////////////////////////////////////////////////////////////
// static BakedOpDispatcher methods
////////////////////////////////////////////////////////////////////////////////

void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
    LOG_ALWAYS_FATAL("unsupported operation");
}

void BakedOpRenderer::onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) {
    info.caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
    Texture* texture = info.getTexture(op.bitmap);
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);
    if (!texture) return;
    const AutoTexture autoCleanup(texture);

    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
    Glop glop;
    GlopBuilder(info.renderState, info.caches, &glop)
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshTexturedUnitQuad(texture->uvMapper)
            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
            .build();
    info.renderGlop(state, glop);
    renderer.renderGlop(state, glop);
}

void BakedOpRenderer::onRectOp(Info& info, const RectOp& op, const BakedOpState& state) {
void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
    Glop glop;
    GlopBuilder(info.renderState, info.caches, &glop)
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshUnitQuad()
            .setFillPaint(*op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewMapUnitToRect(op.unmappedBounds)
            .build();
    info.renderGlop(state, glop);
    renderer.renderGlop(state, glop);
}

void BakedOpRenderer::onSimpleRectsOp(Info& info, const SimpleRectsOp& op, const BakedOpState& state) {
void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
    Glop glop;
    GlopBuilder(info.renderState, info.caches, &glop)
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
            .setFillPaint(*op.paint, state.alpha)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
            .build();
    info.renderGlop(state, glop);
    renderer.renderGlop(state, glop);
}

void BakedOpRenderer::onBeginLayerOp(Info& info, const BeginLayerOp& op, const BakedOpState& state) {
void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
    LOG_ALWAYS_FATAL("unsupported operation");
}

void BakedOpRenderer::onEndLayerOp(Info& info, const EndLayerOp& op, const BakedOpState& state) {
void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
    LOG_ALWAYS_FATAL("unsupported operation");
}

void BakedOpRenderer::onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) {
    Layer* layer = *op.layerHandle;

    // TODO: make this work for HW layers
    layer->setPaint(op.paint);
    layer->setBlend(true);
    float layerAlpha = (layer->getAlpha() / 255.0f) * state.alpha;
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.paint->getAlpha() / 255.0f) * state.alpha;
    const bool tryToSnap = state.computedState.transform.isPureTranslate();
    Glop glop;
    GlopBuilder(info.renderState, info.caches, &glop)
    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
            .setRoundRectClipState(state.roundRectClipState)
            .setMeshTexturedUvQuad(nullptr, layer->texCoords)
            .setFillLayer(layer->getTexture(), layer->getColorFilter(), layerAlpha, layer->getMode(), Blend::ModeOrderSwap::NoSwap)
            .setMeshTexturedUvQuad(nullptr, buffer->texCoords)
            .setFillLayer(buffer->texture, op.paint->getColorFilter(), layerAlpha, PaintUtils::getXfermodeDirect(op.paint), Blend::ModeOrderSwap::NoSwap)
            .setTransform(state.computedState.transform, TransformFlags::None)
            .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
            .build();
    info.renderGlop(state, glop);
    renderer.renderGlop(state, glop);

    // return layer to cache, since each clipped savelayer is only drawn once.
    layer->setConvexMask(nullptr);
    if (!info.caches.layerCache.put(layer)) {
        // Failing to add the layer to the cache should happen only if the layer is too large
        LAYER_LOGD("Deleting layer");
        layer->decStrong(nullptr);
    }
    // destroy and delete, since each clipped saveLayer is only drawn once.
    buffer->texture.deleteTexture();

    // TODO: return texture/offscreenbuffer to cache!
    delete buffer;
}

} // namespace uirenderer
+65 −31
Original line number Diff line number Diff line
@@ -28,48 +28,82 @@ struct Glop;
class Layer;
class RenderState;

class BakedOpRenderer {
/**
 * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
 * encompasses enough information to draw it back on screen (minus paint properties, which are held
 * by LayerOp).
 */
class OffscreenBuffer {
public:
    class Info {
    OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
            uint32_t viewportWidth, uint32_t viewportHeight);

    Texture texture;
    Rect texCoords;
    Region region;
};

/**
 * Main rendering manager for a collection of work - one frame + any contained FBOs.
 *
 * Manages frame and FBO lifecycle, binding the GL framebuffer as appropriate. This is the only
 * place where FBOs are bound, created, and destroyed.
 *
 * All rendering operations will be sent by the Dispatcher, a collection of static methods,
 * which has intentionally limited access to the renderer functionality.
 */
class BakedOpRenderer {
public:
        Info(Caches& caches, RenderState& renderState, bool opaque)
                : renderState(renderState)
                , caches(caches)
                , opaque(opaque) {
    BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque)
            : mRenderState(renderState)
            , mCaches(caches)
            , mOpaque(opaque) {
    }

        void setViewport(uint32_t width, uint32_t height);
    RenderState& renderState() { return mRenderState; }
    Caches& caches() { return mCaches; }

    void startFrame(uint32_t width, uint32_t height);
    void endFrame();
    OffscreenBuffer* startLayer(uint32_t width, uint32_t height);
    void endLayer();

    Texture* getTexture(const SkBitmap* bitmap);

    void renderGlop(const BakedOpState& state, const Glop& glop);
        RenderState& renderState;
        Caches& caches;

        bool didDraw = false;
    bool didDraw() { return mHasDrawn; }
private:
    void setViewport(uint32_t width, uint32_t height);

        Layer* layer = nullptr;
    RenderState& mRenderState;
    Caches& mCaches;
    bool mOpaque;
    bool mHasDrawn = false;

        // where should these live? layer state object?
        bool opaque;
    // render target state - setup by start/end layer/frame
    // only valid to use in between start/end pairs.
    struct {
        GLuint frameBufferId = 0;
        OffscreenBuffer* offscreenBuffer = nullptr;
        uint32_t viewportWidth = 0;
        uint32_t viewportHeight = 0;
        Matrix4 orthoMatrix;
    } mRenderTarget;
};

    static Layer* startLayer(Info& info, uint32_t width, uint32_t height);
    static void endLayer(Info& info);
    static void startFrame(Info& info, uint32_t width, uint32_t height);
    static void endFrame(Info& info);

/**
     * Declare all "onBitmapOp(...)" style function for every op type.
 * Provides all "onBitmapOp(...)" style static methods for every op type, which convert the
 * RecordedOps and their state to Glops, and renders them with the provided BakedOpRenderer.
 *
     * These functions will perform the actual rendering of the individual operations in OpenGL,
     * given the transform/clip and other state built into the BakedOpState object passed in.
 * This dispatcher is separate from the renderer so that the dispatcher / renderer interaction is
 * minimal through public BakedOpRenderer APIs.
 */
    #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info& info, const Type& op, const BakedOpState& state);
    MAP_OPS(BAKED_OP_RENDERER_METHOD);
class BakedOpDispatcher {
public:
    // Declares all "onBitmapOp(...)" style methods for every op type
#define DISPATCH_METHOD(Type) \
        static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state);
    MAP_OPS(DISPATCH_METHOD);
};

}; // namespace uirenderer
+3 −6
Original line number Diff line number Diff line
@@ -277,7 +277,8 @@ void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
    }
}

void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const {
void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const {
    ATRACE_NAME("flush drawing commands");
    for (const BatchBase* batch : mBatches) {
        // TODO: different behavior based on batch->isMerging()
        for (const BakedOpState* op : batch->getOps()) {
@@ -353,10 +354,6 @@ void OpReorderer::deferImpl(const DisplayList& displayList) {
    }
}

void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
    ATRACE_NAME("flush drawing commands");
}

void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
    if (op.renderNode->nothingToDraw()) {
        return;
@@ -435,7 +432,7 @@ void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
            beginLayerOp.localMatrix,
            beginLayerOp.localClipRect,
            beginLayerOp.paint,
            &mLayerReorderers[finishedLayerIndex].layer);
            &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
    BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);

    if (bakedOpState) {
+17 −16
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ namespace uirenderer {
class BakedOpState;
class BatchBase;
class MergingOpBatch;
class OffscreenBuffer;
class OpBatch;
class Rect;

@@ -55,7 +56,7 @@ namespace OpBatchType {
}

class OpReorderer : public CanvasStateClient {
    typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
    typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpDispatcher;

    /**
     * Stores the deferred render operations and state used to compute ordering
@@ -79,7 +80,7 @@ class OpReorderer : public CanvasStateClient {
        void deferMergeableOp(LinearAllocator& allocator,
                BakedOpState* op, batchid_t batchId, mergeid_t mergeId);

        void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const;
        void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers) const;

        bool empty() const {
            return mBatches.empty();
@@ -91,7 +92,7 @@ class OpReorderer : public CanvasStateClient {

        void dump() const;

        Layer* layer = nullptr;
        OffscreenBuffer* offscreenBuffer = nullptr;
        const BeginLayerOp* beginLayerOp = nullptr;
        const uint32_t width;
        const uint32_t height;
@@ -127,15 +128,15 @@ public:
     *
     * For example a BitmapOp would resolve, via the lambda lookup, to calling:
     *
     * StaticReceiver::onBitmapOp(Arg* arg, const BitmapOp& op, const BakedOpState& state);
     * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state);
     */
#define BAKED_OP_RECEIVER(Type) \
    [](void* internalArg, const RecordedOp& op, const BakedOpState& state) { \
        StaticReceiver::on##Type(*(static_cast<Arg*>(internalArg)), static_cast<const Type&>(op), state); \
    [](void* internalRenderer, const RecordedOp& op, const BakedOpState& state) { \
        StaticDispatcher::on##Type(*(static_cast<Renderer*>(internalRenderer)), static_cast<const Type&>(op), state); \
    },
    template <typename StaticReceiver, typename Arg>
    void replayBakedOps(Arg& arg) {
        static BakedOpReceiver receivers[] = {
    template <typename StaticDispatcher, typename Renderer>
    void replayBakedOps(Renderer& renderer) {
        static BakedOpDispatcher receivers[] = {
            MAP_OPS(BAKED_OP_RECEIVER)
        };

@@ -144,16 +145,16 @@ public:
        for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
            LayerReorderer& layer = mLayerReorderers[i];
            if (!layer.empty()) {
                layer.layer = StaticReceiver::startLayer(arg, layer.width, layer.height);
                layer.replayBakedOpsImpl((void*)&arg, receivers);
                StaticReceiver::endLayer(arg);
                layer.offscreenBuffer = renderer.startLayer(layer.width, layer.height);
                layer.replayBakedOpsImpl((void*)&renderer, receivers);
                renderer.endLayer();
            }
        }

        const LayerReorderer& fbo0 = mLayerReorderers[0];
        StaticReceiver::startFrame(arg, fbo0.width, fbo0.height);
        fbo0.replayBakedOpsImpl((void*)&arg, receivers);
        StaticReceiver::endFrame(arg);
        renderer.startFrame(fbo0.width, fbo0.height);
        fbo0.replayBakedOpsImpl((void*)&renderer, receivers);
        renderer.endFrame();
    }

    void dump() const {
@@ -178,7 +179,7 @@ private:

    void deferImpl(const DisplayList& displayList);

    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
    void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);

    /**
     * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
+3 −3
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ class SkPaint;
namespace android {
namespace uirenderer {

class Layer;
class OffscreenBuffer;
class RenderNode;
struct Vertex;

@@ -137,12 +137,12 @@ struct EndLayerOp : RecordedOp {
};

struct LayerOp : RecordedOp {
    LayerOp(BASE_PARAMS, Layer** layerHandle)
    LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
            : SUPER(LayerOp)
            , layerHandle(layerHandle) {}
    // Records a handle to the Layer object, since the Layer itself won't be
    // constructed until after this operation is constructed.
    Layer** layerHandle;
    OffscreenBuffer** layerHandle;
};

}; // namespace uirenderer
Loading