Loading libs/hwui/BakedOpRenderer.cpp +115 −95 Original line number Diff line number Diff line Loading @@ -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 Loading libs/hwui/BakedOpRenderer.h +65 −31 Original line number Diff line number Diff line Loading @@ -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 Loading libs/hwui/OpReorderer.cpp +3 −6 Original line number Diff line number Diff line Loading @@ -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()) { Loading Loading @@ -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; Loading Loading @@ -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) { Loading libs/hwui/OpReorderer.h +17 −16 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ namespace uirenderer { class BakedOpState; class BatchBase; class MergingOpBatch; class OffscreenBuffer; class OpBatch; class Rect; Loading @@ -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 Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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) }; Loading @@ -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 { Loading @@ -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. Loading libs/hwui/RecordedOp.h +3 −3 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ class SkPaint; namespace android { namespace uirenderer { class Layer; class OffscreenBuffer; class RenderNode; struct Vertex; Loading Loading @@ -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 Loading
libs/hwui/BakedOpRenderer.cpp +115 −95 Original line number Diff line number Diff line Loading @@ -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 Loading
libs/hwui/BakedOpRenderer.h +65 −31 Original line number Diff line number Diff line Loading @@ -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 Loading
libs/hwui/OpReorderer.cpp +3 −6 Original line number Diff line number Diff line Loading @@ -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()) { Loading Loading @@ -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; Loading Loading @@ -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) { Loading
libs/hwui/OpReorderer.h +17 −16 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ namespace uirenderer { class BakedOpState; class BatchBase; class MergingOpBatch; class OffscreenBuffer; class OpBatch; class Rect; Loading @@ -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 Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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) }; Loading @@ -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 { Loading @@ -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. Loading
libs/hwui/RecordedOp.h +3 −3 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ class SkPaint; namespace android { namespace uirenderer { class Layer; class OffscreenBuffer; class RenderNode; struct Vertex; Loading Loading @@ -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