Ejemplo n.º 1
0
static bool get_geometry(const SkPath& path, const SkMatrix& m, PLSVertices& triVertices, 
                         PLSVertices& quadVertices, GrResourceProvider* resourceProvider,
                         SkRect bounds) {
    SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
    SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, m, bounds);
    int contourCnt;
    int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, tol);
    if (maxPts <= 0) {
        return 0;
    }
    SkPath linesOnlyPath;
    linesOnlyPath.setFillType(path.getFillType());
    SkSTArray<15, SkPoint, true> quadPoints;
    SkPath::Iter iter(path, true);
    bool done = false;
    while (!done) {
        SkPoint pts[4];
        SkPath::Verb verb = iter.next(pts);
        switch (verb) {
            case SkPath::kMove_Verb:
                SkASSERT(quadPoints.count() % 3 == 0);
                for (int i = 0; i < quadPoints.count(); i += 3) {
                    add_quad(&quadPoints[i], quadVertices);
                }
                quadPoints.reset();
                m.mapPoints(&pts[0], 1);
                linesOnlyPath.moveTo(pts[0]);
                break;
            case SkPath::kLine_Verb:
                m.mapPoints(&pts[1], 1);
                linesOnlyPath.lineTo(pts[1]);
                break;
            case SkPath::kQuad_Verb:
                m.mapPoints(pts, 3);
                linesOnlyPath.lineTo(pts[2]);
                quadPoints.push_back(pts[0]);
                quadPoints.push_back(pts[1]);
                quadPoints.push_back(pts[2]);
                break;
            case SkPath::kCubic_Verb: {
                m.mapPoints(pts, 4);
                SkSTArray<15, SkPoint, true> quads;
                GrPathUtils::convertCubicToQuads(pts, kCubicTolerance, &quads);
                int count = quads.count();
                for (int q = 0; q < count; q += 3) {
                    linesOnlyPath.lineTo(quads[q + 2]);
                    quadPoints.push_back(quads[q]);
                    quadPoints.push_back(quads[q + 1]);
                    quadPoints.push_back(quads[q + 2]);
                }
                break;
            }
            case SkPath::kConic_Verb: {
                m.mapPoints(pts, 3);
                SkScalar weight = iter.conicWeight();
                SkAutoConicToQuads converter;
                const SkPoint* quads = converter.computeQuads(pts, weight, kConicTolerance);
                int count = converter.countQuads();
                for (int i = 0; i < count; ++i) {
                    linesOnlyPath.lineTo(quads[2 * i + 2]);
                    quadPoints.push_back(quads[2 * i]);
                    quadPoints.push_back(quads[2 * i + 1]);
                    quadPoints.push_back(quads[2 * i + 2]);
                }
                break;
            }
            case SkPath::kClose_Verb:
                linesOnlyPath.close();
                break;
            case SkPath::kDone_Verb:
                done = true;
                break;
            default: SkASSERT(false);
        }
    }
    SkASSERT(quadPoints.count() % 3 == 0);
    for (int i = 0; i < quadPoints.count(); i += 3) {
        add_quad(&quadPoints[i], quadVertices);
    }

    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    GrUniqueKey key;
    GrUniqueKey::Builder builder(&key, kDomain, 2);
    builder[0] = path.getGenerationID();
    builder[1] = path.getFillType();
    builder.finish();
    GrTessellator::WindingVertex* windingVertices;
    int triVertexCount = GrTessellator::PathToVertices(linesOnlyPath, 0, bounds, &windingVertices);
    if (triVertexCount > 0) {
        for (int i = 0; i < triVertexCount; i += 3) {
            SkPoint p1 = windingVertices[i].fPos;
            SkPoint p2 = windingVertices[i + 1].fPos;
            SkPoint p3 = windingVertices[i + 2].fPos;
            int winding = windingVertices[i].fWinding;
            SkASSERT(windingVertices[i + 1].fWinding == winding);
            SkASSERT(windingVertices[i + 2].fWinding == winding);
            SkScalar cross = (p2 - p1).cross(p3 - p1);
            SkPoint bloated[3] = { p1, p2, p3 };
            if (cross < 0.0f) {
                SkTSwap(p1, p3);
            }
            if (bloat_tri(bloated)) {
                triVertices.push_back({ bloated[0], p1, p2, p3, winding });
                triVertices.push_back({ bloated[1], p1, p2, p3, winding });
                triVertices.push_back({ bloated[2], p1, p2, p3, winding });
            }
            else {
                SkScalar minX = SkTMin(p1.fX, SkTMin(p2.fX, p3.fX)) - 1.0f;
                SkScalar minY = SkTMin(p1.fY, SkTMin(p2.fY, p3.fY)) - 1.0f;
                SkScalar maxX = SkTMax(p1.fX, SkTMax(p2.fX, p3.fX)) + 1.0f;
                SkScalar maxY = SkTMax(p1.fY, SkTMax(p2.fY, p3.fY)) + 1.0f;
                triVertices.push_back({ { minX, minY }, p1, p2, p3, winding });
                triVertices.push_back({ { maxX, minY }, p1, p2, p3, winding });
                triVertices.push_back({ { minX, maxY }, p1, p2, p3, winding });
                triVertices.push_back({ { maxX, minY }, p1, p2, p3, winding });
                triVertices.push_back({ { maxX, maxY }, p1, p2, p3, winding });
                triVertices.push_back({ { minX, maxY }, p1, p2, p3, winding });
            }
        }
        delete[] windingVertices;
    }
    return triVertexCount > 0 || quadVertices.count() > 0;
}
static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
                            double t2s, double t2e, SkIntersections* i, bool* subDivide) {
    SkDQuad hull = q1.subDivide(t1s, t1e);
    SkDLine line = {{hull[2], hull[0]}};
    const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
    const size_t kTestCount = SK_ARRAY_COUNT(testLines);
    SkSTArray<kTestCount * 2, double, true> tsFound;
    for (size_t index = 0; index < kTestCount; ++index) {
        SkIntersections rootTs;
        rootTs.allowNear(false);
        int roots = rootTs.intersect(q2, *testLines[index]);
        for (int idx2 = 0; idx2 < roots; ++idx2) {
            double t = rootTs[0][idx2];
#ifdef SK_DEBUG
            SkDPoint qPt = q2.ptAtT(t);
            SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
            SkASSERT(qPt.approximatelyEqual(lPt));
#endif
            if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
                continue;
            }
            tsFound.push_back(rootTs[0][idx2]);
        }
    }
    int tCount = tsFound.count();
    if (tCount <= 0) {
        return true;
    }
    double tMin, tMax;
    if (tCount == 1) {
        tMin = tMax = tsFound[0];
    } else {
        SkASSERT(tCount > 1);
        SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
        tMin = tsFound[0];
        tMax = tsFound[tsFound.count() - 1];
    }
    SkDPoint end = q2.ptAtT(t2s);
    bool startInTriangle = hull.pointInHull(end);
    if (startInTriangle) {
        tMin = t2s;
    }
    end = q2.ptAtT(t2e);
    bool endInTriangle = hull.pointInHull(end);
    if (endInTriangle) {
        tMax = t2e;
    }
    int split = 0;
    SkDVector dxy1, dxy2;
    if (tMin != tMax || tCount > 2) {
        dxy2 = q2.dxdyAtT(tMin);
        for (int index = 1; index < tCount; ++index) {
            dxy1 = dxy2;
            dxy2 = q2.dxdyAtT(tsFound[index]);
            double dot = dxy1.dot(dxy2);
            if (dot < 0) {
                split = index - 1;
                break;
            }
        }
    }
    if (split == 0) {  // there's one point
        if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
            return true;
        }
        i->swap();
        return is_linear_inner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
    }
    // At this point, we have two ranges of t values -- treat each separately at the split
    bool result;
    if (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
        result = true;
    } else {
        i->swap();
        result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
    }
    if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
        result = true;
    } else {
        i->swap();
        result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
    }
    return result;
}
Ejemplo n.º 3
0
void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
                         const SkDRect& bounds2) {
    SkDLine line;
    int t1Index = start ? 0 : 3;
    double testT = (double) !start;
   // don't bother if the two cubics are connnected
    static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
    static const int kMaxLineCubicIntersections = 3;
    SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
    line[0] = cubic1[t1Index];
    // this variant looks for intersections with the end point and lines parallel to other points
    for (int index = 0; index < kPointsInCubic; ++index) {
        if (index == t1Index) {
            continue;
        }
        SkDVector dxy1 = cubic1[index] - line[0];
        dxy1 /= SkDCubic::gPrecisionUnit;
        line[1] = line[0] + dxy1;
        SkDRect lineBounds;
        lineBounds.setBounds(line);
        if (!bounds2.intersects(&lineBounds)) {
            continue;
        }
        SkIntersections local;
        if (!local.intersect(cubic2, line)) {
            continue;
        }
        for (int idx2 = 0; idx2 < local.used(); ++idx2) {
            double foundT = local[0][idx2];
            if (approximately_less_than_zero(foundT)
                    || approximately_greater_than_one(foundT)) {
                continue;
            }
            if (local.pt(idx2).approximatelyEqual(line[0])) {
                if (swapped()) {  // FIXME: insert should respect swap
                    insert(foundT, testT, line[0]);
                } else {
                    insert(testT, foundT, line[0]);
                }
            } else {
                tVals.push_back(foundT);
            }
        }
    }
    if (tVals.count() == 0) {
        return;
    }
    SkTQSort<double>(tVals.begin(), tVals.end() - 1);
    double tMin1 = start ? 0 : 1 - LINE_FRACTION;
    double tMax1 = start ? LINE_FRACTION : 1;
    int tIdx = 0;
    do {
        int tLast = tIdx;
        while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
            ++tLast;
        }
        double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
        double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
        int lastUsed = used();
        if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
            ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
        }
        if (lastUsed == used()) {
            tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
            tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
            if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
                ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
            }
        }
        tIdx = tLast + 1;
    } while (tIdx < tVals.count());
    return;
}
Ejemplo n.º 4
0
bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
    SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
    SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
             fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored]);
    SkASSERT(fCoverageCountBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());

    // Here we build a single instance buffer to share with every internal batch.
    //
    // CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive
    // type is further divided into instances that require a scissor and those that don't. This
    // leaves us with 3*2 = 6 independent instance arrays to build for the GPU.
    //
    // Rather than place each instance array in its own GPU buffer, we allocate a single
    // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in
    // our draw calls to direct the GPU to the applicable elements within a given array.
    //
    // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is
    // straightforward. Start with triangles and quadratics. They both view the instance buffer as
    // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other.
    fBaseInstances[0].fTriangles = 0;
    fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles +
                                   fTotalPrimitiveCounts[0].fTriangles;
    fBaseInstances[0].fQuadratics = fBaseInstances[1].fTriangles +
                                    fTotalPrimitiveCounts[1].fTriangles;
    fBaseInstances[1].fQuadratics = fBaseInstances[0].fQuadratics +
                                    fTotalPrimitiveCounts[0].fQuadratics;
    int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;

    // Wound triangles and cubics both view the same instance buffer as an array of
    // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start
    // them on the first index that will not overwrite previous TriPointInstance data.
    int quadBaseIdx =
            GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
    fBaseInstances[0].fWeightedTriangles = quadBaseIdx;
    fBaseInstances[1].fWeightedTriangles = fBaseInstances[0].fWeightedTriangles +
                                        fTotalPrimitiveCounts[0].fWeightedTriangles;
    fBaseInstances[0].fCubics = fBaseInstances[1].fWeightedTriangles +
                                fTotalPrimitiveCounts[1].fWeightedTriangles;
    fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
    fBaseInstances[0].fConics = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
    fBaseInstances[1].fConics = fBaseInstances[0].fConics + fTotalPrimitiveCounts[0].fConics;
    int quadEndIdx = fBaseInstances[1].fConics + fTotalPrimitiveCounts[1].fConics;

    fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
                                            quadEndIdx * sizeof(QuadPointInstance));
    if (!fInstanceBuffer) {
        return false;
    }

    TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map());
    QuadPointInstance* quadPointInstanceData =
            reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
    SkASSERT(quadPointInstanceData);

    PathInfo* nextPathInfo = fPathsInfo.begin();
    float atlasOffsetX = 0.0, atlasOffsetY = 0.0;
    Sk2f atlasOffset;
    PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
    PrimitiveTallies* currIndices = nullptr;
    SkSTArray<256, int32_t, true> currFan;
    bool currFanIsTessellated = false;

    const SkTArray<SkPoint, true>& pts = fGeometry.points();
    int ptsIdx = -1;
    int nextConicWeightIdx = 0;

    // Expand the ccpr verbs into GPU instance buffers.
    for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
        switch (verb) {
            case GrCCGeometry::Verb::kBeginPath:
                SkASSERT(currFan.empty());
                currIndices = &instanceIndices[(int)nextPathInfo->scissorMode()];
                atlasOffsetX = static_cast<float>(nextPathInfo->atlasOffsetX());
                atlasOffsetY = static_cast<float>(nextPathInfo->atlasOffsetY());
                atlasOffset = {atlasOffsetX, atlasOffsetY};
                currFanIsTessellated = nextPathInfo->hasFanTessellation();
                if (currFanIsTessellated) {
                    emit_tessellated_fan(nextPathInfo->fanTessellation(),
                                         nextPathInfo->fanTessellationCount(), atlasOffset,
                                         triPointInstanceData, quadPointInstanceData, currIndices);
                }
                ++nextPathInfo;
                continue;

            case GrCCGeometry::Verb::kBeginContour:
                SkASSERT(currFan.empty());
                ++ptsIdx;
                if (!currFanIsTessellated) {
                    currFan.push_back(ptsIdx);
                }
                continue;

            case GrCCGeometry::Verb::kLineTo:
                ++ptsIdx;
                if (!currFanIsTessellated) {
                    SkASSERT(!currFan.empty());
                    currFan.push_back(ptsIdx);
                }
                continue;

            case GrCCGeometry::Verb::kMonotonicQuadraticTo:
                triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset);
                ptsIdx += 2;
                if (!currFanIsTessellated) {
                    SkASSERT(!currFan.empty());
                    currFan.push_back(ptsIdx);
                }
                continue;

            case GrCCGeometry::Verb::kMonotonicCubicTo:
                quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX,
                                                                  atlasOffsetY);
                ptsIdx += 3;
                if (!currFanIsTessellated) {
                    SkASSERT(!currFan.empty());
                    currFan.push_back(ptsIdx);
                }
                continue;

            case GrCCGeometry::Verb::kMonotonicConicTo:
                quadPointInstanceData[currIndices->fConics++].setW(
                        &pts[ptsIdx], atlasOffset, fGeometry.getConicWeight(nextConicWeightIdx));
                ptsIdx += 2;
                ++nextConicWeightIdx;
                if (!currFanIsTessellated) {
                    SkASSERT(!currFan.empty());
                    currFan.push_back(ptsIdx);
                }
                continue;

            case GrCCGeometry::Verb::kEndClosedContour:  // endPt == startPt.
                if (!currFanIsTessellated) {
                    SkASSERT(!currFan.empty());
                    currFan.pop_back();
                }
            // fallthru.
            case GrCCGeometry::Verb::kEndOpenContour:  // endPt != startPt.
                SkASSERT(!currFanIsTessellated || currFan.empty());
                if (!currFanIsTessellated && currFan.count() >= 3) {
                    int fanSize = currFan.count();
                    // Reserve space for emit_recursive_fan. Technically this can grow to
                    // fanSize + log3(fanSize), but we approximate with log2.
                    currFan.push_back_n(SkNextLog2(fanSize));
                    SkDEBUGCODE(TriPointInstance* end =)
                            emit_recursive_fan(pts, currFan, 0, fanSize, atlasOffset,
                                               triPointInstanceData + currIndices->fTriangles);
                    currIndices->fTriangles += fanSize - 2;
                    SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
                }
                currFan.reset();
                continue;
        }
    }
Ejemplo n.º 5
0
bool GrVkCopyManager::copySurfaceAsDraw(GrVkGpu* gpu,
                                        GrSurface* dst, GrSurfaceOrigin dstOrigin,
                                        GrSurface* src, GrSurfaceOrigin srcOrigin,
                                        const SkIRect& srcRect, const SkIPoint& dstPoint,
                                        bool canDiscardOutsideDstRect) {
    // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the
    // swizzle.
    if (gpu->caps()->shaderCaps()->configOutputSwizzle(src->config()) !=
        gpu->caps()->shaderCaps()->configOutputSwizzle(dst->config())) {
        return false;
    }

    GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(dst->asRenderTarget());
    if (!rt) {
        return false;
    }

    GrVkTexture* srcTex = static_cast<GrVkTexture*>(src->asTexture());
    if (!srcTex) {
        return false;
    }

    if (VK_NULL_HANDLE == fVertShaderModule) {
        SkASSERT(VK_NULL_HANDLE == fFragShaderModule &&
                 nullptr == fPipelineLayout &&
                 nullptr == fVertexBuffer.get() &&
                 nullptr == fUniformBuffer.get());
        if (!this->createCopyProgram(gpu)) {
            SkDebugf("Failed to create copy program.\n");
            return false;
        }
    }
    SkASSERT(fPipelineLayout);

    GrVkResourceProvider& resourceProv = gpu->resourceProvider();

    GrVkCopyPipeline* pipeline = resourceProv.findOrCreateCopyPipeline(rt,
                                                                       fShaderStageInfo,
                                                                       fPipelineLayout->layout());
    if (!pipeline) {
        return false;
    }

    // UPDATE UNIFORM DESCRIPTOR SET
    int w = srcRect.width();
    int h = srcRect.height();

    // dst rect edges in NDC (-1 to 1)
    int dw = dst->width();
    int dh = dst->height();
    float dx0 = 2.f * dstPoint.fX / dw - 1.f;
    float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f;
    float dy0 = 2.f * dstPoint.fY / dh - 1.f;
    float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f;
    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
        dy0 = -dy0;
        dy1 = -dy1;
    }


    float sx0 = (float)srcRect.fLeft;
    float sx1 = (float)(srcRect.fLeft + w);
    float sy0 = (float)srcRect.fTop;
    float sy1 = (float)(srcRect.fTop + h);
    int sh = src->height();
    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
        sy0 = sh - sy0;
        sy1 = sh - sy1;
    }
    // src rect edges in normalized texture space (0 to 1).
    int sw = src->width();
    sx0 /= sw;
    sx1 /= sw;
    sy0 /= sh;
    sy1 /= sh;

    float uniData[] = { dx1 - dx0, dy1 - dy0, dx0, dy0,    // posXform
                        sx1 - sx0, sy1 - sy0, sx0, sy0 };  // texCoordXform

    fUniformBuffer->updateData(gpu, uniData, sizeof(uniData), nullptr);

    const GrVkDescriptorSet* uniformDS = resourceProv.getUniformDescriptorSet();
    SkASSERT(uniformDS);

    VkDescriptorBufferInfo uniBufferInfo;
    uniBufferInfo.buffer = fUniformBuffer->buffer();
    uniBufferInfo.offset = fUniformBuffer->offset();
    uniBufferInfo.range = fUniformBuffer->size();

    VkWriteDescriptorSet descriptorWrites;
    descriptorWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    descriptorWrites.pNext = nullptr;
    descriptorWrites.dstSet = uniformDS->descriptorSet();
    descriptorWrites.dstBinding = GrVkUniformHandler::kGeometryBinding;
    descriptorWrites.dstArrayElement = 0;
    descriptorWrites.descriptorCount = 1;
    descriptorWrites.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    descriptorWrites.pImageInfo = nullptr;
    descriptorWrites.pBufferInfo = &uniBufferInfo;
    descriptorWrites.pTexelBufferView = nullptr;

    GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
                                                        1,
                                                        &descriptorWrites,
                                                        0, nullptr));

    // UPDATE SAMPLER DESCRIPTOR SET
    const GrVkDescriptorSet* samplerDS =
        gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);

    GrSamplerState samplerState = GrSamplerState::ClampNearest();

    GrVkSampler* sampler = resourceProv.findOrCreateCompatibleSampler(
            samplerState, GrVkYcbcrConversionInfo());

    VkDescriptorImageInfo imageInfo;
    memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
    imageInfo.sampler = sampler->sampler();
    imageInfo.imageView = srcTex->textureView()->imageView();
    imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

    VkWriteDescriptorSet writeInfo;
    memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
    writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    writeInfo.pNext = nullptr;
    writeInfo.dstSet = samplerDS->descriptorSet();
    writeInfo.dstBinding = 0;
    writeInfo.dstArrayElement = 0;
    writeInfo.descriptorCount = 1;
    writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    writeInfo.pImageInfo = &imageInfo;
    writeInfo.pBufferInfo = nullptr;
    writeInfo.pTexelBufferView = nullptr;

    GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
                                                        1,
                                                        &writeInfo,
                                                        0, nullptr));

    VkDescriptorSet vkDescSets[] = { uniformDS->descriptorSet(), samplerDS->descriptorSet() };

    GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(srcTex->asRenderTarget());
    if (texRT) {
        gpu->resolveRenderTargetNoFlush(texRT);
    }

    // TODO: Make tighter bounds and then adjust bounds for origin and granularity if we see
    //       any perf issues with using the whole bounds
    SkIRect bounds = SkIRect::MakeWH(rt->width(), rt->height());

    // Change layouts of rt and texture. We aren't blending so we don't need color attachment read
    // access for blending.
    GrVkImage* targetImage = rt->msaaImage() ? rt->msaaImage() : rt;
    VkAccessFlags dstAccessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    if (!canDiscardOutsideDstRect) {
        // We need to load the color attachment so need to be able to read it.
        dstAccessFlags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
    }
    targetImage->setImageLayout(gpu,
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                                dstAccessFlags,
                                VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                                false);

    srcTex->setImageLayout(gpu,
                           VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                           VK_ACCESS_SHADER_READ_BIT,
                           VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                           false);

    GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
    if (stencil) {
        GrVkStencilAttachment* vkStencil = (GrVkStencilAttachment*)stencil;
        // We aren't actually using the stencil but we still load and store it so we need
        // appropriate barriers.
        // TODO: Once we refactor surface and how we conntect stencil to RTs, we should not even
        // have the stencil on this render pass if possible.
        vkStencil->setImageLayout(gpu,
                                  VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
                                  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
                                  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
                                  VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
                                  false);
    }

    VkAttachmentLoadOp loadOp = canDiscardOutsideDstRect ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
                                                         : VK_ATTACHMENT_LOAD_OP_LOAD;
    GrVkRenderPass::LoadStoreOps vkColorOps(loadOp, VK_ATTACHMENT_STORE_OP_STORE);
    GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD,
                                              VK_ATTACHMENT_STORE_OP_STORE);
    const GrVkRenderPass* renderPass;
    const GrVkResourceProvider::CompatibleRPHandle& rpHandle = rt->compatibleRenderPassHandle();
    if (rpHandle.isValid()) {
        renderPass = gpu->resourceProvider().findRenderPass(rpHandle,
                                                            vkColorOps,
                                                            vkStencilOps);
    } else {
        renderPass = gpu->resourceProvider().findRenderPass(*rt,
                                                            vkColorOps,
                                                            vkStencilOps);
    }

    SkASSERT(renderPass->isCompatible(*rt->simpleRenderPass()));

    GrVkPrimaryCommandBuffer* cmdBuffer = gpu->currentCommandBuffer();
    cmdBuffer->beginRenderPass(gpu, renderPass, nullptr, *rt, bounds, true);

    GrVkSecondaryCommandBuffer* secondary = gpu->cmdPool()->findOrCreateSecondaryCommandBuffer(gpu);
    if (!secondary) {
        return false;
    }
    secondary->begin(gpu, rt->framebuffer(), renderPass);

    secondary->bindPipeline(gpu, pipeline);

    // Uniform DescriptorSet, Sampler DescriptorSet, and vertex shader uniformBuffer
    SkSTArray<3, const GrVkRecycledResource*> descriptorRecycledResources;
    descriptorRecycledResources.push_back(uniformDS);
    descriptorRecycledResources.push_back(samplerDS);
    descriptorRecycledResources.push_back(fUniformBuffer->resource());

    // One sampler, texture view, and texture
    SkSTArray<3, const GrVkResource*> descriptorResources;
    descriptorResources.push_back(sampler);
    descriptorResources.push_back(srcTex->textureView());
    descriptorResources.push_back(srcTex->resource());

    secondary->bindDescriptorSets(gpu,
                                  descriptorRecycledResources,
                                  descriptorResources,
                                  fPipelineLayout,
                                  0,
                                  2,
                                  vkDescSets,
                                  0,
                                  nullptr);

    // Set Dynamic viewport and stencil
    // We always use one viewport the size of the RT
    VkViewport viewport;
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = SkIntToScalar(rt->width());
    viewport.height = SkIntToScalar(rt->height());
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;
    secondary->setViewport(gpu, 0, 1, &viewport);

    // We assume the scissor is not enabled so just set it to the whole RT
    VkRect2D scissor;
    scissor.extent.width = rt->width();
    scissor.extent.height = rt->height();
    scissor.offset.x = 0;
    scissor.offset.y = 0;
    secondary->setScissor(gpu, 0, 1, &scissor);

    secondary->bindInputBuffer(gpu, 0, fVertexBuffer.get());
    secondary->draw(gpu, 4, 1, 0, 0);
    secondary->end(gpu);
    cmdBuffer->executeCommands(gpu, secondary);
    cmdBuffer->endRenderPass(gpu);
    secondary->unref(gpu);

    // Release all temp resources which should now be reffed by the cmd buffer
    pipeline->unref(gpu);
    uniformDS->unref(gpu);
    samplerDS->unref(gpu);
    sampler->unref(gpu);
    renderPass->unref(gpu);

    return true;
}