namespace android {
namespace uirenderer {
-void BakedOpRenderer::Info::setViewport(uint32_t width, uint32_t height) {
- viewportWidth = width;
- viewportHeight = height;
- orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
-
- renderState.setViewport(width, height);
- renderState.blend().syncEnabled();
-}
-
-Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
- Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
- if (!texture) {
- return caches.textureCache.get(bitmap);
- }
- return texture;
-}
-
-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;
+////////////////////////////////////////////////////////////////////////////////
+// OffscreenBuffer
+////////////////////////////////////////////////////////////////////////////////
+
+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;
+
+ caches.textureState().activateTexture(0);
+ glGenTextures(1, &texture.id);
+ caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
+
+ 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);
-
- // 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);
- }
+ renderer.renderGlop(state, glop);
+
+ // destroy and delete, since each clipped saveLayer is only drawn once.
+ buffer->texture.deleteTexture();
+
+ // TODO: return texture/offscreenbuffer to cache!
+ delete buffer;
}
} // namespace uirenderer
class Layer;
class RenderState;
+/**
+ * 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:
+ 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:
- class Info {
- 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; }
- Texture* getTexture(const SkBitmap* bitmap);
+ void startFrame(uint32_t width, uint32_t height);
+ void endFrame();
+ OffscreenBuffer* startLayer(uint32_t width, uint32_t height);
+ void endLayer();
- void renderGlop(const BakedOpState& state, const Glop& glop);
- RenderState& renderState;
- Caches& caches;
+ Texture* getTexture(const SkBitmap* bitmap);
- bool didDraw = false;
+ void renderGlop(const BakedOpState& state, const Glop& glop);
+ 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;
- };
-
- 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.
- *
- * 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.
- */
- #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info& info, const Type& op, const BakedOpState& state);
- MAP_OPS(BAKED_OP_RENDERER_METHOD);
+ } mRenderTarget;
+};
+
+/**
+ * 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.
+ *
+ * This dispatcher is separate from the renderer so that the dispatcher / renderer interaction is
+ * minimal through public BakedOpRenderer APIs.
+ */
+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
}
}
-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()) {
}
}
-void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
- ATRACE_NAME("flush drawing commands");
-}
-
void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) {
return;
beginLayerOp.localMatrix,
beginLayerOp.localClipRect,
beginLayerOp.paint,
- &mLayerReorderers[finishedLayerIndex].layer);
+ &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
class BakedOpState;
class BatchBase;
class MergingOpBatch;
+class OffscreenBuffer;
class OpBatch;
class Rect;
}
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
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();
void dump() const;
- Layer* layer = nullptr;
+ OffscreenBuffer* offscreenBuffer = nullptr;
const BeginLayerOp* beginLayerOp = nullptr;
const uint32_t width;
const uint32_t height;
*
* 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)
};
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 {
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.
namespace android {
namespace uirenderer {
-class Layer;
+class OffscreenBuffer;
class RenderNode;
struct Vertex;
};
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
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
OpReorderer reorderer(200, 200, *sReorderingDisplayList);
- MicroBench::DoNotOptimize(&reorderer);
- BakedOpRenderer::Info info(caches, renderState, true);
- reorderer.replayBakedOps<BakedOpRenderer>(info);
+ BakedOpRenderer renderer(caches, renderState, true);
+ reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+ MicroBench::DoNotOptimize(&renderer);
}
StopBenchmarkTiming();
});
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include <set>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <utils/Mutex.h>
-#include <utils/Functor.h>
-#include <utils/RefBase.h>
-#include <private/hwui/DrawGlInfo.h>
-#include <renderstate/Blend.h>
-
#include "AssetAtlas.h"
#include "Caches.h"
#include "Glop.h"
+#include "renderstate/Blend.h"
#include "renderstate/MeshState.h"
#include "renderstate/PixelBufferState.h"
#include "renderstate/Scissor.h"
#include "renderstate/Stencil.h"
#include "utils/Macros.h"
+#include <set>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <ui/Region.h>
+#include <utils/Mutex.h>
+#include <utils/Functor.h>
+#include <utils/RefBase.h>
+#include <private/hwui/DrawGlInfo.h>
+
namespace android {
namespace uirenderer {
// wrapper of Caches for users to migrate to.
class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
+ friend class renderthread::RenderThread;
+ friend class Caches;
public:
void onGLContextCreated();
void onGLContextDestroyed();
GLuint genFramebuffer();
void deleteFramebuffer(GLuint fbo);
-
void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
void debugOverdraw(bool enable, bool clear);
Stencil& stencil() { return *mStencil; }
void dump();
-private:
- friend class renderthread::RenderThread;
- friend class Caches;
+private:
void interruptForFunctorInvoke();
void resumeFromFunctorInvoke();
void assertOnGLThread();
#if HWUI_NEW_OPS
OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
- BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
+ BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
// TODO: profiler().draw(mCanvas);
- reorderer.replayBakedOps<BakedOpRenderer>(info);
+ reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
- bool drew = info.didDraw;
+ bool drew = renderer.didDraw();
#else
mCanvas->prepareDirty(frame.width(), frame.height(),
namespace android {
namespace uirenderer {
+
/**
- * Class that redirects static operation dispatch to virtual methods on a Client class.
+ * Virtual class implemented by each test to redirect static operation / state transitions to
+ * virtual methods.
*
- * The client is recreated for every op (so data cannot be persisted between operations), but the
- * virtual dispatch allows for default behaviors to be specified without enumerating each operation
- * for every test.
+ * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
+ * and allows Renderer vs Dispatching behavior to be merged.
*
* onXXXOp methods fail by default - tests should override ops they expect
* startLayer fails by default - tests should override if expected
* startFrame/endFrame do nothing by default - tests should override to intercept
*/
-template<class CustomClient, class Arg>
-class TestReceiver {
+class TestRendererBase {
public:
-#define CLIENT_METHOD(Type) \
- virtual void on##Type(Arg&, const Type&, const BakedOpState&) { ADD_FAILURE(); }
- class Client {
- public:
- virtual ~Client() {};
- MAP_OPS(CLIENT_METHOD)
-
- virtual Layer* startLayer(Arg& info, uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
- virtual void endLayer(Arg& info) { ADD_FAILURE(); }
- virtual void startFrame(Arg& info, uint32_t width, uint32_t height) {}
- virtual void endFrame(Arg& info) {}
- };
+ virtual ~TestRendererBase() {}
+ virtual OffscreenBuffer* startLayer(uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
+ virtual void endLayer() { ADD_FAILURE(); }
+ virtual void startFrame(uint32_t width, uint32_t height) {}
+ virtual void endFrame() {}
+
+ // define virtual defaults for direct
+#define BASE_OP_METHOD(Type) \
+ virtual void on##Type(const Type&, const BakedOpState&) { ADD_FAILURE(); }
+ MAP_OPS(BASE_OP_METHOD)
+ int getIndex() { return mIndex; }
+
+protected:
+ int mIndex = 0;
+};
+/**
+ * Dispatches all static methods to similar formed methods on renderer, which fail by default but
+ * are overriden by subclasses per test.
+ */
+class TestDispatcher {
+public:
#define DISPATCHER_METHOD(Type) \
- static void on##Type(Arg& arg, const Type& op, const BakedOpState& state) { \
- CustomClient client; client.on##Type(arg, op, state); \
- }
- MAP_OPS(DISPATCHER_METHOD)
-
- static Layer* startLayer(Arg& info, uint32_t width, uint32_t height) {
- CustomClient client;
- return client.startLayer(info, width, height);
- }
- static void endLayer(Arg& info) {
- CustomClient client;
- client.endLayer(info);
- }
- static void startFrame(Arg& info, uint32_t width, uint32_t height) {
- CustomClient client;
- client.startFrame(info, width, height);
- }
- static void endFrame(Arg& info) {
- CustomClient client;
- client.endFrame(info);
+ static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
+ renderer.on##Type(op, state); \
}
+ MAP_OPS(DISPATCHER_METHOD);
};
-class Info {
-public:
- int index = 0;
-};
-// Receiver class which will fail if it receives any ops
-class FailReceiver : public TestReceiver<FailReceiver, Info>::Client {};
+class FailRenderer : public TestRendererBase {};
-class SimpleReceiver : public TestReceiver<SimpleReceiver, Info>::Client {
+class SimpleTestRenderer : public TestRendererBase {
public:
- void startFrame(Info& info, uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, info.index++);
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
EXPECT_EQ(100u, width);
EXPECT_EQ(200u, height);
}
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, info.index++);
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
}
- void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, info.index++);
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
}
- void endFrame(Info& info) override {
- EXPECT_EQ(3, info.index++);
+ void endFrame() override {
+ EXPECT_EQ(3, mIndex++);
}
};
TEST(OpReorderer, simple) {
});
OpReorderer reorderer(100, 200, *dl);
- Info info;
- reorderer.replayBakedOps<TestReceiver<SimpleReceiver, Info>>(info);
- EXPECT_EQ(4, info.index); // 2 ops + start + end
+ SimpleTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
});
OpReorderer reorderer(200, 200, *dl);
- Info info;
- reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
+ FailRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
}
static int SIMPLE_BATCHING_LOOPS = 5;
-class SimpleBatchingReceiver : public TestReceiver<SimpleBatchingReceiver, Info>::Client {
+class SimpleBatchingTestRenderer : public TestRendererBase {
public:
- void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(info.index++ >= SIMPLE_BATCHING_LOOPS);
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
}
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(info.index++ < SIMPLE_BATCHING_LOOPS);
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
}
};
TEST(OpReorderer, simpleBatching) {
OpReorderer reorderer(200, 200, *dl);
- Info info;
- reorderer.replayBakedOps<TestReceiver<SimpleBatchingReceiver, Info>>(info);
- EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, info.index); // 2 x loops ops, because no merging (TODO: force no merging)
+ SimpleBatchingTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging)
}
-class RenderNodeReceiver : public TestReceiver<RenderNodeReceiver, Info>::Client {
+class RenderNodeTestRenderer : public TestRendererBase {
public:
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
- switch(info.index++) {
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ switch(mIndex++) {
case 0:
EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
- Info info;
- reorderer.replayBakedOps<TestReceiver<RenderNodeReceiver, Info>>(info);
+ RenderNodeTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-class ClippedReceiver : public TestReceiver<ClippedReceiver, Info>::Client {
+class ClippedTestRenderer : public TestRendererBase {
public:
- void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, info.index++);
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
EXPECT_TRUE(state.computedState.transform.isIdentity());
OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, nodes);
- Info info;
- reorderer.replayBakedOps<TestReceiver<ClippedReceiver, Info>>(info);
+ ClippedTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-class SaveLayerSimpleReceiver : public TestReceiver<SaveLayerSimpleReceiver, Info>::Client {
+class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
- Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, info.index++);
+ OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
EXPECT_EQ(180u, width);
EXPECT_EQ(180u, height);
return nullptr;
}
- void endLayer(Info& info) override {
- EXPECT_EQ(2, info.index++);
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
}
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, info.index++);
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
expectedTransform.loadTranslate(-10, -10, 0);
EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
}
- void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, info.index++);
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(3, mIndex++);
EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
EXPECT_TRUE(state.computedState.transform.isIdentity());
OpReorderer reorderer(200, 200, *dl);
- Info info;
- reorderer.replayBakedOps<TestReceiver<SaveLayerSimpleReceiver, Info>>(info);
- EXPECT_EQ(4, info.index);
+ SaveLayerSimpleTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
}
* - startLayer1, rect1, drawLayer2, endLayer1
* - startFrame, layerOp1, endFrame
*/
-class SaveLayerNestedReceiver : public TestReceiver<SaveLayerNestedReceiver, Info>::Client {
+class SaveLayerNestedTestRenderer : public TestRendererBase {
public:
- Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
- const int index = info.index++;
+ OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+ const int index = mIndex++;
if (index == 0) {
EXPECT_EQ(400u, width);
EXPECT_EQ(400u, height);
- return (Layer*) 0x400;
+ return (OffscreenBuffer*) 0x400;
} else if (index == 3) {
EXPECT_EQ(800u, width);
EXPECT_EQ(800u, height);
- return (Layer*) 0x800;
+ return (OffscreenBuffer*) 0x800;
} else { ADD_FAILURE(); }
- return (Layer*) nullptr;
+ return (OffscreenBuffer*) nullptr;
}
- void endLayer(Info& info) override {
- int index = info.index++;
+ void endLayer() override {
+ int index = mIndex++;
EXPECT_TRUE(index == 2 || index == 6);
}
- void startFrame(Info& info, uint32_t width, uint32_t height) override {
- EXPECT_EQ(7, info.index++);
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(7, mIndex++);
}
- void endFrame(Info& info) override {
- EXPECT_EQ(9, info.index++);
+ void endFrame() override {
+ EXPECT_EQ(9, mIndex++);
}
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
- const int index = info.index++;
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
if (index == 1) {
EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
} else if (index == 4) {
EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
} else { ADD_FAILURE(); }
}
- void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
- const int index = info.index++;
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
if (index == 5) {
- EXPECT_EQ((Layer*)0x400, *op.layerHandle);
+ EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
} else if (index == 8) {
- EXPECT_EQ((Layer*)0x800, *op.layerHandle);
+ EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
} else { ADD_FAILURE(); }
}
OpReorderer reorderer(800, 800, *dl);
- Info info;
- reorderer.replayBakedOps<TestReceiver<SaveLayerNestedReceiver, Info>>(info);
- EXPECT_EQ(10, info.index);
+ SaveLayerNestedTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(10, renderer.getIndex());
}
TEST(OpReorderer, saveLayerContentRejection) {
canvas.restore();
});
OpReorderer reorderer(200, 200, *dl);
- Info info;
+ FailRenderer renderer;
// should see no ops, even within the layer, since the layer should be rejected
- reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
}
} // namespace uirenderer