void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
            const DefaultGeoProc& gp = args.fGP.cast<DefaultGeoProc>();
            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

            // emit attributes
            varyingHandler->emitAttributes(gp);

            // Setup pass through color
            if (gp.hasVertexColor()) {
                GrGLSLVarying varying(kHalf4_GrSLType);
                varyingHandler->addVarying("color", &varying);

                // There are several optional steps to process the color. Start with the attribute:
                vertBuilder->codeAppendf("half4 color = %s;", gp.inColor()->fName);

                // Linearize
                if (gp.linearizeColor()) {
                    SkString srgbFuncName;
                    static const GrShaderVar gSrgbArgs[] = {
                        GrShaderVar("x", kHalf_GrSLType),
                    };
                    vertBuilder->emitFunction(kHalf_GrSLType,
                                              "srgb_to_linear",
                                              SK_ARRAY_COUNT(gSrgbArgs),
                                              gSrgbArgs,
                                              "return (x <= 0.04045) ? (x / 12.92) "
                                              ": pow((x + 0.055) / 1.055, 2.4);",
                                              &srgbFuncName);
                    vertBuilder->codeAppendf("color = half4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);",
                                             srgbFuncName.c_str(), gp.inColor()->fName,
                                             srgbFuncName.c_str(), gp.inColor()->fName,
                                             srgbFuncName.c_str(), gp.inColor()->fName,
                                             gp.inColor()->fName);
                }

                // For SkColor, do a red/blue swap and premul
                if (gp.fFlags & kColorAttributeIsSkColor_GPFlag) {
                    vertBuilder->codeAppend("color = half4(color.a * color.bgr, color.a);");
                }

                // Do color-correction to destination gamut
                if (gp.linearizeColor()) {
                    fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(),
                                               kVertex_GrShaderFlag);
                    if (fColorSpaceHelper.isValid()) {
                        SkString xformedColor;
                        vertBuilder->appendColorGamutXform(&xformedColor, "color",
                                                           &fColorSpaceHelper);
                        vertBuilder->codeAppendf("color = %s;", xformedColor.c_str());
                    }
                }
                vertBuilder->codeAppendf("%s = color;\n", varying.vsOut());
                fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
            } else {
                this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
                                        &fColorUniform);
            }

            // Setup position
            this->writeOutputPosition(vertBuilder,
                                      uniformHandler,
                                      gpArgs,
                                      gp.inPosition()->fName,
                                      gp.viewMatrix(),
                                      &fViewMatrixUniform);

            if (gp.hasExplicitLocalCoords()) {
                // emit transforms with explicit local coords
                this->emitTransforms(vertBuilder,
                                     varyingHandler,
                                     uniformHandler,
                                     gp.inLocalCoords()->asShaderVar(),
                                     gp.localMatrix(),
                                     args.fFPCoordTransformHandler);
            } else {
                // emit transforms with position
                this->emitTransforms(vertBuilder,
                                     varyingHandler,
                                     uniformHandler,
                                     gp.inPosition()->asShaderVar(),
                                     gp.localMatrix(),
                                     args.fFPCoordTransformHandler);
            }

            // Setup coverage as pass through
            if (gp.hasVertexCoverage()) {
                fragBuilder->codeAppendf("half alpha = 1.0;");
                varyingHandler->addPassThroughAttribute(gp.inCoverage(), "alpha");
                fragBuilder->codeAppendf("%s = half4(alpha);", args.fOutputCoverage);
            } else if (gp.coverage() == 0xff) {
                fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
            } else {
                const char* fragCoverage;
                fCoverageUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
                                                              kHalf_GrSLType,
                                                              "Coverage",
                                                              &fragCoverage);
                fragBuilder->codeAppendf("%s = half4(%s);", args.fOutputCoverage, fragCoverage);
            }
        }
Exemple #2
0
    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
        const auto& proc = args.fGP.cast<Processor>();
        bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
        bool hasPerspective = (proc.fFlags & Flags::kHasPerspective);
        bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords);
        SkASSERT(useHWDerivatives == hasPerspective);

        SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));

        // Emit the vertex shader.
        GrGLSLVertexBuilder* v = args.fVertBuilder;

        GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
        varyings->emitAttributes(proc);
        varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
                                          GrGLSLVaryingHandler::Interpolation::kCanBeFlat);

        // Unpack vertex attribs.
        v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
        v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");

        // Identify our radii.
        v->codeAppend("float2 radii;");
        v->codeAppend("radii.x = dot(radii_selector, radii_x);");
        v->codeAppend("radii.y = dot(radii_selector, radii_y);");
        v->codeAppendf("bool is_arc_section = (radii.x > 0);");
        v->codeAppendf("radii = abs(radii);");

        // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
        // [-1,-1,+1,+1] space.
        v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");

        // Emit transforms.
        GrShaderVar localCoord("", kFloat2_GrSLType);
        if (hasLocalCoords) {
            v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
                                               "local_rect.zw * (1 + vertexpos)) * .5;");
            localCoord.set(kFloat2_GrSLType, "localcoord");
        }
        this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
                             args.fFPCoordTransformHandler);

        // Transform to device space.
        if (!hasPerspective) {
            v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
            v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
            gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
        } else {
            v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
            v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
            gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
        }

        // Determine normalized arc coordinates for the implicit function.
        GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
        varyings->addVarying("arccoord", &arcCoord);
        v->codeAppendf("if (is_arc_section) {");
        v->codeAppendf(    "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
        if (!useHWDerivatives) {
            // The gradient is order-1: Interpolate it across arccoord.zw.
            // This doesn't work with perspective.
            SkASSERT(!hasPerspective);
            v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
            v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
                           arcCoord.vsOut(), arcCoord.vsOut());
        }
        v->codeAppendf("} else {");
        if (useHWDerivatives) {
            v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
        } else {
            v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
        }
        v->codeAppendf("}");

        // Emit the fragment shader.
        GrGLSLFPFragmentBuilder* f = args.fFragBuilder;

        f->codeAppendf("%s = half4(1);", args.fOutputCoverage);

        // If x,y == 0, then we are drawing a triangle that does not track an arc.
        f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
        f->codeAppendf(    "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
        if (GrAAType::kMSAA == proc.fAAType) {
            using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
            if (!useHWDerivatives) {
                f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
                f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
            } else {
                f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
            }
        } else {
            f->codeAppendf("if (fn > 0) {");
            f->codeAppendf(    "%s = half4(0);", args.fOutputCoverage);
            f->codeAppendf("}");
        }
        f->codeAppendf("}");
    }
void GLSLPathProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
    using InstanceAttribs = GrCCPRPathProcessor::InstanceAttribs;
    const GrCCPRPathProcessor& proc = args.fGP.cast<GrCCPRPathProcessor>();
    GrGLSLUniformHandler* uniHandler = args.fUniformHandler;
    GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;

    const char* atlasAdjust;
    fAtlasAdjustUniform = uniHandler->addUniform(
            kVertex_GrShaderFlag,
            kVec2f_GrSLType, kHigh_GrSLPrecision, "atlas_adjust", &atlasAdjust);

    varyingHandler->emitAttributes(proc);

    GrGLSLVertToFrag texcoord(kVec2f_GrSLType);
    GrGLSLVertToFrag color(kVec4f_GrSLType);
    varyingHandler->addVarying("texcoord", &texcoord, kHigh_GrSLPrecision);
    varyingHandler->addFlatPassThroughAttribute(&proc.getInstanceAttrib(InstanceAttribs::kColor),
                                                args.fOutputColor, kLow_GrSLPrecision);

    // Vertex shader.
    GrGLSLVertexBuilder* v = args.fVertBuilder;

    // Find the intersections of (bloated) devBounds and devBounds45 in order to come up with an
    // octagon that circumscribes the (bloated) path. A vertex is the intersection of two lines:
    // one edge from the path's bounding box and one edge from its 45-degree bounding box.
    v->codeAppendf("highp mat2 N = mat2(%s);", proc.getEdgeNormsAttrib().fName);

    // N[0] is the normal for the edge we are intersecting from the regular bounding box, pointing
    // out of the octagon.
    v->codeAppendf("highp vec2 refpt = (min(N[0].x, N[0].y) < 0) ? %s.xy : %s.zw;",
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds).fName,
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds).fName);
    v->codeAppendf("refpt += N[0] * %f;", kAABloatRadius); // bloat for AA.

    // N[1] is the normal for the edge we are intersecting from the 45-degree bounding box, pointing
    // out of the octagon.
    v->codeAppendf("highp vec2 refpt45 = (N[1].x < 0) ? %s.xy : %s.zw;",
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds45).fName,
                   proc.getInstanceAttrib(InstanceAttribs::kDevBounds45).fName);
    v->codeAppendf("refpt45 *= mat2(.5,.5,-.5,.5);"); // transform back to device space.
    v->codeAppendf("refpt45 += N[1] * %f;", kAABloatRadius); // bloat for AA.

    v->codeAppend ("highp vec2 K = vec2(dot(N[0], refpt), dot(N[1], refpt45));");
    v->codeAppendf("highp vec2 octocoord = K * inverse(N);");

    gpArgs->fPositionVar.set(kVec2f_GrSLType, "octocoord");

    // Convert to atlas coordinates in order to do our texture lookup.
    v->codeAppendf("highp vec2 atlascoord = octocoord + vec2(%s);",
                   proc.getInstanceAttrib(InstanceAttribs::kAtlasOffset).fName);
    if (kTopLeft_GrSurfaceOrigin == proc.atlas()->origin()) {
        v->codeAppendf("%s = atlascoord * %s;", texcoord.vsOut(), atlasAdjust);
    } else {
        SkASSERT(kBottomLeft_GrSurfaceOrigin == proc.atlas()->origin());
        v->codeAppendf("%s = vec2(atlascoord.x * %s.x, 1 - atlascoord.y * %s.y);",
                       texcoord.vsOut(), atlasAdjust, atlasAdjust);
    }

    // Convert to (local) path cordinates.
    v->codeAppendf("highp vec2 pathcoord = inverse(mat2(%s)) * (octocoord - %s);",
                   proc.getInstanceAttrib(InstanceAttribs::kViewMatrix).fName,
                   proc.getInstanceAttrib(InstanceAttribs::kViewTranslate).fName);

    this->emitTransforms(v, varyingHandler, uniHandler, gpArgs->fPositionVar, "pathcoord",
                         args.fFPCoordTransformHandler);

    // Fragment shader.
    GrGLSLPPFragmentBuilder* f = args.fFragBuilder;

    f->codeAppend ("mediump float coverage_count = ");
    f->appendTextureLookup(args.fTexSamplers[0], texcoord.fsIn(), kVec2f_GrSLType);
    f->codeAppend (".a;");

    if (SkPath::kWinding_FillType == proc.fillType()) {
        f->codeAppendf("%s = vec4(min(abs(coverage_count), 1));", args.fOutputCoverage);
    } else {
        SkASSERT(SkPath::kEvenOdd_FillType == proc.fillType());
        f->codeAppend ("mediump float t = mod(abs(coverage_count), 2);");
        f->codeAppendf("%s = vec4(1 - abs(t - 1));", args.fOutputCoverage);
    }
}
Exemple #4
0
    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
        const auto& proc = args.fGP.cast<Processor>();
        bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);

        SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));

        GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
        varyings->emitAttributes(proc);
        varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
                                          GrGLSLVaryingHandler::Interpolation::kCanBeFlat);

        // Emit the vertex shader.
        GrGLSLVertexBuilder* v = args.fVertBuilder;

        // Unpack vertex attribs.
        v->codeAppend("float2 corner = corner_and_radius_outsets.xy;");
        v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;");
        v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;");
        v->codeAppend("float coverage = aa_bloat_and_coverage.z;");
        v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;");

        // Find the amount to bloat each edge for AA (in source space).
        v->codeAppend("float2 pixellength = inversesqrt("
                              "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));");
        v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;");
        v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + "
                                           "abs(normalized_axis_dirs.zw));");
        v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;");

        // Identify our radii.
        v->codeAppend("float4 radii_and_neighbors = radii_selector"
                              "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);");
        v->codeAppend("float2 radii = radii_and_neighbors.xy;");
        v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;");

        v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {");
                          // The rrect is more narrow than an AA coverage ramp. We can't draw as-is
                          // or else opposite AA borders will overlap. Instead, fudge the size up to
                          // the width of a coverage ramp, and then reduce total coverage to make
                          // the rect appear more thin.
        v->codeAppend(    "corner = max(abs(corner), aa_bloatradius) * sign(corner);");
        v->codeAppend(    "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);");
                          // Set radii to zero to ensure we take the "linear coverage" codepath.
                          // (The "coverage" variable only has effect in the linear codepath.)
        v->codeAppend(    "radii = float2(0);");
        v->codeAppend("}");

        v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {");
                          // The radii are very small. Demote this arc to a sharp 90 degree corner.
        v->codeAppend(    "radii = aa_bloatradius;");
                          // Snap octagon vertices to the corner of the bounding box.
        v->codeAppend(    "radius_outset = floor(abs(radius_outset)) * radius_outset;");
        v->codeAppend(    "is_linear_coverage = 1;");
        v->codeAppend("} else {");
                          // Don't let radii get smaller than a pixel.
        v->codeAppend(    "radii = clamp(radii, pixellength, 2 - pixellength);");
        v->codeAppend(    "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);");
                          // Don't let neighboring radii get closer together than 1/16 pixel.
        v->codeAppend(    "float2 spacing = 2 - radii - neighbor_radii;");
        v->codeAppend(    "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));");
        v->codeAppend(    "radii -= extra_pad * .5;");
        v->codeAppend("}");

        // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in
        // normalized [-1,-1,+1,+1] space.
        v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;");
        v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;");

        // Emit transforms.
        GrShaderVar localCoord("", kFloat2_GrSLType);
        if (proc.fFlags & Flags::kHasLocalCoords) {
            v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
                                               "local_rect.zw * (1 + vertexpos)) * .5;");
            localCoord.set(kFloat2_GrSLType, "localcoord");
        }
        this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
                             args.fFPCoordTransformHandler);

        // Transform to device space.
        SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
        v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
        v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
        gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");

        // Setup interpolants for coverage.
        GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType);
        varyings->addVarying("arccoord", &arcCoord);
        v->codeAppend("if (0 != is_linear_coverage) {");
                           // We are a non-corner piece: Set x=0 to indicate built-in coverage, and
                           // interpolate linear coverage across y.
        v->codeAppendf(    "%s.xy = float2(0, coverage);", arcCoord.vsOut());
        v->codeAppend("} else {");
                           // Find the normalized arc coordinates for our corner ellipse.
                           // (i.e., the coordinate system where x^2 + y^2 == 1).
        v->codeAppend(    "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;");
                           // We are a corner piece: Interpolate the arc coordinates for coverage.
                           // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0
                           // instructs the fragment shader to use linear coverage).
        v->codeAppendf(    "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut());
        if (!useHWDerivatives) {
            // The gradient is order-1: Interpolate it across arccoord.zw.
            v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
            v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut());
        }
        v->codeAppend("}");

        // Emit the fragment shader.
        GrGLSLFPFragmentBuilder* f = args.fFragBuilder;

        f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
        f->codeAppendf("half coverage;");
        f->codeAppendf("if (0 == x_plus_1) {");
        f->codeAppendf(    "coverage = half(y);");  // We are a non-arc pixel (linear coverage).
        f->codeAppendf("} else {");
        f->codeAppendf(    "float fn = x_plus_1 * (x_plus_1 - 2);");  // fn = (x+1)*(x-1) = x^2-1
        f->codeAppendf(    "fn = fma(y,y, fn);");  // fn = x^2 + y^2 - 1
        if (useHWDerivatives) {
            f->codeAppendf("float fnwidth = fwidth(fn);");
        } else {
            // The gradient is interpolated across arccoord.zw.
            f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn());
            f->codeAppendf("float fnwidth = abs(gx) + abs(gy);");
        }
        f->codeAppendf(    "half d = half(fn/fnwidth);");
        f->codeAppendf(    "coverage = clamp(.5 - d, 0, 1);");
        f->codeAppendf("}");
        f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage);
    }