Create z reordering boundaries around dispatchDraw

bug:16012254

This means rendernodes with a Z will no longer be drawn at the end of
their parent's DisplayList, but at the end of the associated reorder
region (DisplayListData::Chunk).

Change-Id: Ia033fee9d9a4db567b2a8d5e90fc57a4d0a64544
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index a410aa9..ceea9f8 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -179,6 +179,18 @@
     private static native void nSetHighContrastText(long renderer, boolean highContrastText);
 
     @Override
+    public void insertReorderBarrier() {
+        nInsertReorderBarrier(mRenderer, true);
+    }
+
+    @Override
+    public void insertInorderBarrier() {
+        nInsertReorderBarrier(mRenderer, false);
+    }
+
+    private static native void nInsertReorderBarrier(long renderer, boolean enableReorder);
+
+    @Override
     public int onPreDraw(Rect dirty) {
         if (dirty != null) {
             return nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 19dd583..854e6be 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3072,7 +3072,7 @@
         boolean more = false;
         final long drawingTime = getDrawingTime();
 
-
+        if (usingRenderNodeProperties) canvas.insertReorderBarrier();
         // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
         // draw reordering internally
         final ArrayList<View> preorderedList = usingRenderNodeProperties
@@ -3099,6 +3099,7 @@
                 more |= drawChild(canvas, child, drawingTime);
             }
         }
+        if (usingRenderNodeProperties) canvas.insertInorderBarrier();
 
         if (debugDraw()) {
             onDebugDraw(canvas);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index afcfaf6..6080f2a 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -104,6 +104,12 @@
     renderer->setHighContrastText(highContrastText);
 }
 
+static void android_view_GLES20Canvas_insertReorderBarrier(JNIEnv* env, jobject clazz,
+        jlong rendererPtr, jboolean reorderEnable) {
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
+    renderer->insertReorderBarrier(reorderEnable);
+}
+
 static int android_view_GLES20Canvas_prepare(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jboolean opaque) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
@@ -859,6 +865,7 @@
     { "nDestroyRenderer",   "(J)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
     { "nSetViewport",       "(JII)V",          (void*) android_view_GLES20Canvas_setViewport },
     { "nSetHighContrastText","(JZ)V",          (void*) android_view_GLES20Canvas_setHighContrastText },
+    { "nInsertReorderBarrier","(JZ)V",         (void*) android_view_GLES20Canvas_insertReorderBarrier },
     { "nPrepare",           "(JZ)I",           (void*) android_view_GLES20Canvas_prepare },
     { "nPrepareDirty",      "(JIIIIZ)I",       (void*) android_view_GLES20Canvas_prepareDirty },
     { "nFinish",            "(J)V",            (void*) android_view_GLES20Canvas_finish },
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index f3af8f6..f18694b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -243,6 +243,12 @@
     /** @hide */
     public void setHighContrastText(boolean highContrastText) {}
 
+    /** @hide */
+    public void insertReorderBarrier() {}
+
+    /** @hide */
+    public void insertInorderBarrier() {}
+
     /**
      * @hide
      */
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 6461ee7..d8932ce 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -89,11 +89,9 @@
     layers.clear();
 }
 
-void DisplayListData::addChild(DrawRenderNodeOp* op) {
-    LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawRenderNodeOp with no render node!");
-
-    mChildren.push(op);
+size_t DisplayListData::addChild(DrawRenderNodeOp* op) {
     mReferenceHolders.push(op->renderNode());
+    return mChildren.add(op);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index acfa98e..dea109c 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -115,13 +115,24 @@
  * Data structure that holds the list of commands used in display list stream
  */
 class DisplayListData {
+    friend class DisplayListRenderer;
 public:
+    struct Chunk {
+        // range of included ops in DLD::displayListOps
+        size_t beginOpIndex;
+        size_t endOpIndex;
+
+        // range of included children in DLD::mChildren
+        size_t beginChildIndex;
+        size_t endChildIndex;
+
+        // whether children with non-zero Z in the chunk should be reordered
+        bool reorderChildren;
+    };
+
     DisplayListData();
     ~DisplayListData();
 
-    // allocator into which all ops were allocated
-    LinearAllocator allocator;
-
     // pointers to all ops within display list, pointing into allocator data
     Vector<DisplayListOp*> displayListOps;
 
@@ -138,13 +149,12 @@
     Vector<const SkRegion*> regions;
     Vector<Layer*> layers;
     Vector<Functor*> functors;
-    bool hasDrawOps;
 
-    bool isEmpty() {
-        return !displayListOps.size();
+    const Vector<Chunk>& getChunks() const {
+        return chunks;
     }
 
-    void addChild(DrawRenderNodeOp* childOp);
+    size_t addChild(DrawRenderNodeOp* childOp);
     const Vector<DrawRenderNodeOp*>& children() { return mChildren; }
 
     void refProperty(CanvasPropertyPrimitive* prop) {
@@ -155,12 +165,25 @@
         mReferenceHolders.push(prop);
     }
 
+    size_t getUsedSize() {
+        return allocator.usedSize();
+    }
+    bool isEmpty() {
+        return !hasDrawOps;
+    }
+
 private:
     Vector< sp<VirtualLightRefBase> > mReferenceHolders;
 
     // list of children display lists for quick, non-drawing traversal
     Vector<DrawRenderNodeOp*> mChildren;
 
+    Vector<Chunk> chunks;
+
+    // allocator into which all ops were allocated
+    LinearAllocator allocator;
+    bool hasDrawOps;
+
     void cleanupResources();
 };
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 5f533a7..8818510 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1445,6 +1445,7 @@
 
 class DrawRenderNodeOp : public DrawBoundedOp {
     friend class RenderNode; // grant RenderNode access to info of child
+    friend class DisplayListData; // grant DisplayListData access to info of child
 public:
     DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent)
             : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0),
@@ -1452,13 +1453,14 @@
 
     virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
             bool useQuickReject) {
-        if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+        if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
             mRenderNode->defer(deferStruct, level + 1);
         }
     }
+
     virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
             bool useQuickReject) {
-        if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+        if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
             mRenderNode->replay(replayStruct, level + 1);
         }
     }
@@ -1469,7 +1471,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw Display List %p, flags %#x", mRenderNode, mFlags);
+        OP_LOG("Draw RenderNode %p %s, flags %#x", mRenderNode, mRenderNode->getName(), mFlags);
         if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
             mRenderNode->output(level + 1);
         }
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 9a9c544..5892978 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -33,10 +33,10 @@
 
 DisplayListRenderer::DisplayListRenderer()
     : mCaches(Caches::getInstance())
-    , mDisplayListData(0)
+    , mDisplayListData(NULL)
     , mTranslateX(0.0f)
     , mTranslateY(0.0f)
-    , mHasTranslate(false)
+    , mDeferredBarrierType(kBarrier_None)
     , mHighContrastText(false)
     , mRestoreSaveCount(-1) {
 }
@@ -68,6 +68,7 @@
 
     initializeSaveStack(0, 0, getWidth(), getHeight(), Vector3());
 
+    mDeferredBarrierType = kBarrier_InOrder;
     mDirtyClip = opaque;
     mRestoreSaveCount = -1;
 
@@ -75,8 +76,8 @@
 }
 
 void DisplayListRenderer::finish() {
-    insertRestoreToCount();
-    insertTranslate();
+    flushRestoreToCount();
+    flushTranslate();
 }
 
 void DisplayListRenderer::interrupt() {
@@ -104,13 +105,13 @@
     }
 
     mRestoreSaveCount--;
-    insertTranslate();
+    flushTranslate();
     StatefulBaseRenderer::restore();
 }
 
 void DisplayListRenderer::restoreToCount(int saveCount) {
     mRestoreSaveCount = saveCount;
-    insertTranslate();
+    flushTranslate();
     StatefulBaseRenderer::restoreToCount(saveCount);
 }
 
@@ -123,10 +124,10 @@
 
 void DisplayListRenderer::translate(float dx, float dy, float dz) {
     // ignore dz, not used at defer time
-    mHasTranslate = true;
+    mHasDeferredTranslate = true;
     mTranslateX += dx;
     mTranslateY += dy;
-    insertRestoreToCount();
+    flushRestoreToCount();
     StatefulBaseRenderer::translate(dx, dy, dz);
 }
 
@@ -174,16 +175,12 @@
 }
 
 status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
+    LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
+
     // dirty is an out parameter and should not be recorded,
     // it matters only when replaying the display list
     DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
-    int opIndex = addDrawOp(op);
-    mDisplayListData->addChild(op);
-
-    if (renderNode->stagingProperties().isProjectionReceiver()) {
-        // use staging property, since recording on UI thread
-        mDisplayListData->projectionReceiveIndex = opIndex;
-    }
+    addRenderNodeOp(op);
 
     return DrawGlInfo::kStatusDone;
 }
@@ -428,30 +425,60 @@
     addStateOp(new (alloc()) SetupPaintFilterOp(clearBits, setBits));
 }
 
-void DisplayListRenderer::insertRestoreToCount() {
+void DisplayListRenderer::insertReorderBarrier(bool enableReorder) {
+    flushRestoreToCount();
+    flushTranslate();
+    mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder;
+}
+
+void DisplayListRenderer::flushRestoreToCount() {
     if (mRestoreSaveCount >= 0) {
-        DisplayListOp* op = new (alloc()) RestoreToCountOp(mRestoreSaveCount);
-        mDisplayListData->displayListOps.add(op);
+        addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount));
         mRestoreSaveCount = -1;
     }
 }
 
-void DisplayListRenderer::insertTranslate() {
-    if (mHasTranslate) {
+void DisplayListRenderer::flushTranslate() {
+    if (mHasDeferredTranslate) {
         if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
-            DisplayListOp* op = new (alloc()) TranslateOp(mTranslateX, mTranslateY);
-            mDisplayListData->displayListOps.add(op);
+            addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY));
             mTranslateX = mTranslateY = 0.0f;
         }
-        mHasTranslate = false;
+        mHasDeferredTranslate = false;
     }
 }
 
-int DisplayListRenderer::addStateOp(StateOp* op) {
-    return addOpInternal(op);
+size_t DisplayListRenderer::addOpAndUpdateChunk(DisplayListOp* op) {
+    int insertIndex = mDisplayListData->displayListOps.add(op);
+    if (mDeferredBarrierType != kBarrier_None) {
+        // op is first in new chunk
+        mDisplayListData->chunks.push();
+        DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop();
+        newChunk.beginOpIndex = insertIndex;
+        newChunk.endOpIndex = insertIndex + 1;
+        newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
+
+        int nextChildIndex = mDisplayListData->children().size();
+        newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
+        mDeferredBarrierType = kBarrier_None;
+    } else {
+        // standard case - append to existing chunk
+        mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1;
+    }
+    return insertIndex;
 }
 
-int DisplayListRenderer::addDrawOp(DrawOp* op) {
+size_t DisplayListRenderer::flushAndAddOp(DisplayListOp* op) {
+    flushRestoreToCount();
+    flushTranslate();
+    return addOpAndUpdateChunk(op);
+}
+
+size_t DisplayListRenderer::addStateOp(StateOp* op) {
+    return flushAndAddOp(op);
+}
+
+size_t DisplayListRenderer::addDrawOp(DrawOp* op) {
     Rect localBounds;
     if (op->getLocalBounds(localBounds)) {
         bool rejected = quickRejectConservative(localBounds.left, localBounds.top,
@@ -460,7 +487,22 @@
     }
 
     mDisplayListData->hasDrawOps = true;
-    return addOpInternal(op);
+    return flushAndAddOp(op);
+}
+
+size_t DisplayListRenderer::addRenderNodeOp(DrawRenderNodeOp* op) {
+    int opIndex = addDrawOp(op);
+    int childIndex = mDisplayListData->addChild(op);
+
+    // update the chunk's child indices
+    DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop();
+    chunk.endChildIndex = childIndex + 1;
+
+    if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
+        // use staging property, since recording on UI thread
+        mDisplayListData->projectionReceiveIndex = opIndex;
+    }
+    return opIndex;
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index b5c0159..7350082 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -57,6 +57,8 @@
     DisplayListRenderer();
     virtual ~DisplayListRenderer();
 
+    void insertReorderBarrier(bool enableReorder);
+
     DisplayListData* finishRecording();
 
 // ----------------------------------------------------------------------------
@@ -154,19 +156,27 @@
         mHighContrastText = highContrastText;
     }
 private:
-    void insertRestoreToCount();
-    void insertTranslate();
+    enum DeferredBarrierType {
+        kBarrier_None,
+        kBarrier_InOrder,
+        kBarrier_OutOfOrder,
+    };
+
+    void flushRestoreToCount();
+    void flushTranslate();
+    void flushReorderBarrier();
 
     LinearAllocator& alloc() { return mDisplayListData->allocator; }
 
     // Each method returns final index of op
-    int addStateOp(StateOp* op);
-    int addDrawOp(DrawOp* op);
-    int addOpInternal(DisplayListOp* op) {
-        insertRestoreToCount();
-        insertTranslate();
-        return mDisplayListData->displayListOps.add(op);
-    }
+    size_t addOpAndUpdateChunk(DisplayListOp* op);
+    // flushes any deferred operations, and appends the op
+    size_t flushAndAddOp(DisplayListOp* op);
+
+    size_t addStateOp(StateOp* op);
+    size_t addDrawOp(DrawOp* op);
+    size_t addRenderNodeOp(DrawRenderNodeOp* op);
+
 
     template<class T>
     inline const T* refBuffer(const T* srcBuffer, int32_t count) {
@@ -277,7 +287,8 @@
 
     float mTranslateX;
     float mTranslateY;
-    bool mHasTranslate;
+    bool mHasDeferredTranslate;
+    DeferredBarrierType mDeferredBarrierType;
     bool mHighContrastText;
 
     int mRestoreSaveCount;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 977744f..658265d 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -95,6 +95,7 @@
     properties().debugOutputProperties(level);
     int flags = DisplayListOp::kOpLogFlag_Recurse;
     if (mDisplayListData) {
+        // TODO: consider printing the chunk boundaries here
         for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
             mDisplayListData->displayListOps[i]->output(level, flags);
         }
@@ -106,10 +107,10 @@
 int RenderNode::getDebugSize() {
     int size = sizeof(RenderNode);
     if (mStagingDisplayListData) {
-        size += mStagingDisplayListData->allocator.usedSize();
+        size += mStagingDisplayListData->getUsedSize();
     }
     if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
-        size += mDisplayListData->allocator.usedSize();
+        size += mDisplayListData->getUsedSize();
     }
     return size;
 }
@@ -593,15 +594,16 @@
     issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
 }
 
-void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
-    if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return;
+void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk,
+        Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
+    if (chunk.beginChildIndex == chunk.endChildIndex) return;
 
-    for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
+    for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
         DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
         RenderNode* child = childOp->mRenderNode;
         float childZ = child->properties().getZ();
 
-        if (!MathUtils::isZero(childZ)) {
+        if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
             zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
             childOp->mSkipInOrderDraw = true;
         } else if (!child->properties().getProjectBackwards()) {
@@ -610,7 +612,7 @@
         }
     }
 
-    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+    // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
     std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
 }
 
@@ -871,32 +873,35 @@
             handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
                     renderer.getSaveCount() - 1, properties().getClipToBounds());
         } else {
-            Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
-            buildZSortedChildList(zTranslatedNodes);
-
-            // for 3d root, draw children with negative z values
-            int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler);
-
             DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-            const int saveCountOffset = renderer.getSaveCount() - 1;
-            const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
-            const int size = static_cast<int>(mDisplayListData->displayListOps.size());
-            for (int i = 0; i < size; i++) {
-                DisplayListOp *op = mDisplayListData->displayListOps[i];
+            for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
+                const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
 
+                Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
+                buildZSortedChildList(chunk, zTranslatedNodes);
+
+                // for 3d root, draw children with negative z values
+                int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes,
+                        renderer, handler);
+                const int saveCountOffset = renderer.getSaveCount() - 1;
+                const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+
+                for (int opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
+                    DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
 #if DEBUG_DISPLAY_LIST
-                op->output(level + 1);
+                    op->output(level + 1);
 #endif
-                logBuffer.writeCommand(level, op->name());
-                handler(op, saveCountOffset, properties().getClipToBounds());
+                    logBuffer.writeCommand(level, op->name());
+                    handler(op, saveCountOffset, properties().getClipToBounds());
 
-                if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
-                    issueOperationsOfProjectedChildren(renderer, handler);
+                    if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && opIndex == projectionReceiveIndex)) {
+                        issueOperationsOfProjectedChildren(renderer, handler);
+                    }
                 }
-            }
 
-            // for 3d root, draw children with positive z values
-            issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler);
+                // for 3d root, draw children with positive z values
+                issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler);
+            }
         }
     }
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index afa17d5..18402b2 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -114,7 +114,7 @@
     ANDROID_API int getDebugSize();
 
     bool isRenderable() const {
-        return mDisplayListData && mDisplayListData->hasDrawOps;
+        return mDisplayListData && !mDisplayListData->isEmpty();
     }
 
     bool hasProjectionReceiver() const {
@@ -199,7 +199,8 @@
     template <class T>
     inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
 
-    void buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
+    void buildZSortedChildList(const DisplayListData::Chunk& chunk,
+            Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
 
     template<class T>
     inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);