Use texture barriers to read directly from the RT
authorcdalton <cdalton@nvidia.com>
Wed, 29 Apr 2015 21:17:00 +0000 (14:17 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 29 Apr 2015 21:17:00 +0000 (14:17 -0700)
Updates GrXferProcessor to read directly from the RT texture when
texture barriers are supported and it needs to know the dst color.
Also adds the notion of an Xfer barrier and uses it to issue texture
barriers when the XP will read the RT.

BUG=skia:

Review URL: https://codereview.chromium.org/1040303002

include/gpu/GrXferProcessor.h
src/gpu/GrDrawTarget.cpp
src/gpu/GrGpu.h
src/gpu/GrTargetCommands.cpp
src/gpu/GrTargetCommands.h
src/gpu/GrTest.cpp
src/gpu/GrXferProcessor.cpp
src/gpu/gl/GrGLGpu.cpp
src/gpu/gl/GrGLGpu.h
tests/GLProgramsTest.cpp

index 411231d..8ef897c 100644 (file)
@@ -48,6 +48,14 @@ enum GrBlendCoeff {
 };
 
 /**
+ * Barriers for blending. When a shader reads the dst directly, an Xfer barrier is sometimes
+ * required after a pixel has been written, before it can be safely read again.
+ */
+enum GrXferBarrierType {
+    kTexture_GrXferBarrierType, //<! Required when a shader reads and renders to the same texture.
+};
+
+/**
  * GrXferProcessor is responsible for implementing the xfer mode that blends the src color and dst
  * color. It does this by emitting fragment shader code and controlling the fixed-function blend
  * state. The inputs to its shader code are the final computed src color and fractional pixel
@@ -124,6 +132,14 @@ public:
                                       GrColor* overrideColor,
                                       const GrDrawTargetCaps& caps) = 0;
 
+    /**
+     * Returns whether this XP will require an Xfer barrier on the given rt. If true, outBarrierType
+     * is updated to contain the type of barrier needed.
+     */
+    bool willNeedXferBarrier(const GrRenderTarget* rt,
+                             const GrDrawTargetCaps& caps,
+                             GrXferBarrierType* outBarrierType) const;
+
     struct BlendInfo {
         void reset() {
             fSrcBlend = kOne_GrBlendCoeff;
index e52e4f9..d868bcb 100644 (file)
@@ -78,8 +78,20 @@ bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuil
     if (!pipelineBuilder.willXPNeedDstCopy(*this->caps(), colorPOI, coveragePOI)) {
         return true;
     }
-    SkIRect copyRect;
+
     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
+
+    if (this->caps()->textureBarrierSupport()) {
+        if (GrTexture* rtTex = rt->asTexture()) {
+            // The render target is a texture, se we can read from it directly in the shader. The XP
+            // will be responsible to detect this situation and request a texture barrier.
+            dstCopy->setTexture(rtTex);
+            dstCopy->setOffset(0, 0);
+            return true;
+        }
+    }
+
+    SkIRect copyRect;
     pipelineBuilder.clip().getConservativeBounds(rt, &copyRect);
 
     if (drawBounds) {
index 5784781..b2dbec6 100644 (file)
@@ -304,6 +304,9 @@ public:
                              const SkIRect& srcRect,
                              const SkIPoint& dstPoint) = 0;
 
+    // Called before certain draws in order to guarantee coherent results from dst reads.
+    virtual void xferBarrier(GrXferBarrierType) = 0;
+
     struct DrawArgs {
         typedef GrDrawTarget::DrawInfo DrawInfo;
         DrawArgs(const GrPrimitiveProcessor* primProc,
index c448db9..24ee32d 100644 (file)
@@ -341,6 +341,10 @@ void GrTargetCommands::CopySurface::execute(GrGpu* gpu, const SetState*) {
     gpu->copySurface(this->dst(), this->src(), fSrcRect, fDstPoint);
 }
 
+void GrTargetCommands::XferBarrier::execute(GrGpu* gpu, const SetState* state) {
+    gpu->xferBarrier(fBarrierType);
+}
+
 GrTargetCommands::Cmd* GrTargetCommands::recordCopySurface(GrSurface* dst,
                                                            GrSurface* src,
                                                            const SkIRect& srcRect,
@@ -375,6 +379,8 @@ bool GrTargetCommands::setupPipelineAndShouldDraw(GrInOrderDrawBuffer* iodb,
         fPrevState = ss;
         iodb->recordTraceMarkersIfNecessary(ss);
     }
+
+    this->recordXferBarrierIfNecessary(iodb, pipelineInfo);
     return true;
 }
 
@@ -398,6 +404,25 @@ bool GrTargetCommands::setupPipelineAndShouldDraw(GrInOrderDrawBuffer* iodb,
         fPrevState = ss;
         iodb->recordTraceMarkersIfNecessary(ss);
     }
+
+    this->recordXferBarrierIfNecessary(iodb, pipelineInfo);
     return true;
 }
 
+void GrTargetCommands::recordXferBarrierIfNecessary(GrInOrderDrawBuffer* iodb,
+                                                    const GrDrawTarget::PipelineInfo& info) {
+    SkASSERT(fPrevState);
+    const GrXferProcessor& xp = *fPrevState->getXferProcessor();
+    GrRenderTarget* rt = fPrevState->getRenderTarget();
+
+    GrXferBarrierType barrierType;
+    if (!xp.willNeedXferBarrier(rt, *iodb->caps(), &barrierType)) {
+        return;
+    }
+
+    XferBarrier* xb = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, XferBarrier, ());
+    xb->fBarrierType = barrierType;
+
+    iodb->recordTraceMarkersIfNecessary(xb);
+}
+
index cff9898..4ab41db 100644 (file)
@@ -56,6 +56,7 @@ public:
             kDrawPath_CmdType          = 6,
             kDrawPaths_CmdType         = 7,
             kDrawBatch_CmdType         = 8,
+            kXferBarrier_CmdType       = 9,
         };
 
         Cmd(CmdType type) : fMarkerID(-1), fType(type) {}
@@ -151,6 +152,8 @@ private:
                                                           GrBatch*,
                                                           const GrDrawTarget::PipelineInfo&);
 
+    void recordXferBarrierIfNecessary(GrInOrderDrawBuffer*, const GrDrawTarget::PipelineInfo&);
+
     struct Draw : public Cmd {
         Draw(const GrDrawTarget::DrawInfo& info) : Cmd(kDraw_CmdType), fInfo(info) {}
 
@@ -276,6 +279,12 @@ private:
         const GrPipeline* getPipeline() const {
             return reinterpret_cast<const GrPipeline*>(fPipeline.get());
         }
+        GrRenderTarget* getRenderTarget() const {
+            return this->getPipeline()->getRenderTarget();
+        }
+        const GrXferProcessor* getXferProcessor() const {
+            return this->getPipeline()->getXferProcessor();
+        }
 
         void execute(GrGpu*, const SetState*) override;
 
@@ -303,6 +312,14 @@ private:
         GrBatchTarget*         fBatchTarget;
     };
 
+    struct XferBarrier : public Cmd {
+        XferBarrier() : Cmd(kXferBarrier_CmdType) {}
+
+        void execute(GrGpu*, const SetState*) override;
+
+        GrXferBarrierType   fBarrierType;
+    };
+
      static const int kCmdBufferInitialSizeInBytes = 8 * 1024;
 
      typedef void* TCmdAlign; // This wouldn't be enough align if a command used long double.
index 3c5d3dd..23739f2 100644 (file)
@@ -170,6 +170,8 @@ public:
         return false;
     }
 
+    void xferBarrier(GrXferBarrierType) override {}
+
 private:
     void onResetContext(uint32_t resetBits) override {}
 
index de08ef0..42ac153 100644 (file)
@@ -32,6 +32,19 @@ void GrXferProcessor::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBu
     this->onGetGLProcessorKey(caps, b);
 }
 
+bool GrXferProcessor::willNeedXferBarrier(const GrRenderTarget* rt,
+                                          const GrDrawTargetCaps& caps,
+                                          GrXferBarrierType* outBarrierType) const {
+    if (static_cast<const GrSurface*>(rt) == this->getDstCopyTexture()) {
+        // Texture barriers are required when a shader reads and renders to the same texture.
+        SkASSERT(rt);
+        SkASSERT(caps.textureBarrierSupport());
+        *outBarrierType = kTexture_GrXferBarrierType;
+        return true;
+    }
+    return false;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GrXferProcessor* GrXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI,
index 2c46f9b..4421b3c 100644 (file)
@@ -2727,6 +2727,15 @@ bool GrGLGpu::canCopySurface(const GrSurface* dst,
     return false;
 }
 
+void GrGLGpu::xferBarrier(GrXferBarrierType type) {
+    switch (type) {
+        case kTexture_GrXferBarrierType:
+            SkASSERT(this->caps()->textureBarrierSupport());
+            GL_CALL(TextureBarrier());
+            return;
+    }
+}
+
 void GrGLGpu::didAddGpuTraceMarker() {
     if (this->caps()->gpuTracingSupport()) {
         const GrTraceMarkerSet& markerArray = this->getActiveTraceMarkers();
index d833ca5..5357142 100644 (file)
@@ -105,6 +105,8 @@ public:
                         const SkIRect& srcRect,
                         const SkIPoint& dstPoint) override;
 
+    void xferBarrier(GrXferBarrierType) override;
+
     void buildProgramDesc(GrProgramDesc*,
                           const GrPrimitiveProcessor&,
                           const GrPipeline&,
index bc3da6d..c12e5e8 100644 (file)
@@ -304,6 +304,12 @@ bool GrDrawTarget::programUnitTest(int maxStages) {
         if (pipeline.mustSkip()) {
             continue;
         }
+
+        GrXferBarrierType barrierType;
+        if (pipeline.getXferProcessor()->willNeedXferBarrier(rt, *gpu->caps(), &barrierType)) {
+            gpu->xferBarrier(barrierType);
+        }
+
         GrBatchTracker bt;
         primProc->initBatchTracker(&bt, pipeline.getInitBatchTracker());