void GrGLConvolutionEffect::emitCode(EmitArgs& args) { const GrConvolutionEffect& ce = args.fFp.cast<GrConvolutionEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fImageIncrementUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "ImageIncrement"); if (ce.useBounds()) { fBoundsUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "Bounds"); } int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); fKernelUni = uniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "Kernel", width); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);", args.fOutputColor); const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni); const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); fragBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;", coords2D.c_str(), ce.radius(), imgInc); // Manually unroll loop because some drivers don't; yields 20-30% speedup. for (int i = 0; i < width; i++) { SkString index; SkString kernelIndex; index.appendS32(i); kernel.appendArrayAccess(index.c_str(), &kernelIndex); if (ce.useBounds()) { // We used to compute a bool indicating whether we're in bounds or not, cast it to a // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems // to have a bug that caused corruption. const char* bounds = uniformHandler->getUniformCStr(fBoundsUni); const char* component = ce.direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x"; fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", component, bounds, component, bounds); } fragBuilder->codeAppendf("\t\t%s += ", args.fOutputColor); fragBuilder->appendTextureLookup(args.fSamplers[0], "coord"); fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str()); if (ce.useBounds()) { fragBuilder->codeAppend("}"); } fragBuilder->codeAppendf("\t\tcoord += %s;\n", imgInc); } SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fragBuilder->codeAppend(modulate.c_str()); }
void GrGLSLShaderBuilder::appendTextureLookup(SkString* out, const GrGLSLTextureSampler& sampler, const char* coordName, GrSLType varyingType) const { const GrGLSLCaps* glslCaps = fProgramBuilder->glslCaps(); GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler(); GrSLType samplerType = uniformHandler->getUniformVariable(sampler.fSamplerUniform).getType(); if (samplerType == kSampler2DRect_GrSLType) { if (varyingType == kVec2f_GrSLType) { out->appendf("%s(%s, textureSize(%s) * %s)", GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()), uniformHandler->getUniformCStr(sampler.fSamplerUniform), uniformHandler->getUniformCStr(sampler.fSamplerUniform), coordName); } else { out->appendf("%s(%s, vec3(textureSize(%s) * %s.xy, %s.z))", GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()), uniformHandler->getUniformCStr(sampler.fSamplerUniform), uniformHandler->getUniformCStr(sampler.fSamplerUniform), coordName, coordName); } } else { out->appendf("%s(%s, %s)", GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()), uniformHandler->getUniformCStr(sampler.fSamplerUniform), coordName); } // This refers to any swizzling we may need to get from some backend internal format to the // format used in GrPixelConfig. If this is implemented by the GrGpu object, then swizzle will // be rgba. For shader prettiness we omit the swizzle rather than appending ".rgba". const GrSwizzle& configSwizzle = glslCaps->configTextureSwizzle(sampler.config()); if (configSwizzle != GrSwizzle::RGBA()) { out->appendf(".%s", configSwizzle.c_str()); } }
void emitCode(EmitArgs& args) override { const TwoPointConicalEffect& effect = args.fFp.cast<TwoPointConicalEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; this->emitUniforms(uniformHandler, effect); fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "Conical2FSParams"); SkString p0; // r0 for radial case, r0^2 for strip case p0.appendf("%s", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); const char* tName = "t"; // the gradient GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); const char* p = coords2D.c_str(); if (effect.getType() == Type::kRadial) { char sign = effect.diffRadius() < 0 ? '-' : '+'; fragBuilder->codeAppendf("half %s = %clength(%s) - %s;", tName, sign, p, p0.c_str()); } else { // output will default to transparent black (we simply won't write anything // else to it if invalid, instead of discarding or returning prematurely) fragBuilder->codeAppendf("%s = half4(0.0,0.0,0.0,0.0);", args.fOutputColor); fragBuilder->codeAppendf("half temp = %s - %s.y * %s.y;", p0.c_str(), p, p); fragBuilder->codeAppendf("if (temp >= 0) {"); fragBuilder->codeAppendf("half %s = %s.x + sqrt(temp);", tName, p); } this->emitColor(fragBuilder, uniformHandler, args.fShaderCaps, effect, tName, args.fOutputColor, args.fInputColor, args.fTexSamplers); if (effect.getType() != Type::kRadial) { fragBuilder->codeAppendf("}"); } }
void emitCode(EmitArgs& args) override { const TwoPointConicalEffect& effect = args.fFp.cast<TwoPointConicalEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; this->emitUniforms(uniformHandler, effect); fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "Conical2FSParams"); SkString p0; // 1 / r1 SkString p1; // f = focalX = r0 / (r0 - r1) p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); const char* tName = "t"; // the gradient GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); const char* p = coords2D.c_str(); if (effect.isFocalOnCircle()) { fragBuilder->codeAppendf("half x_t = dot(%s, %s) / %s.x;", p, p, p); } else if (effect.isWellBehaved()) { fragBuilder->codeAppendf("half x_t = length(%s) - %s.x * %s;", p, p, p0.c_str()); } else { char sign = (effect.isSwapped() || !effect.isRadiusIncreasing()) ? '-' : ' '; fragBuilder->codeAppendf("half temp = %s.x * %s.x - %s.y * %s.y;", p, p, p, p); // Initialize x_t to illegal state fragBuilder->codeAppendf("half x_t = -1;"); // Only do sqrt if temp >= 0; this is significantly slower than checking temp >= 0 in // the if statement that checks r(t) >= 0. But GPU may break if we sqrt a negative // float. (Although I havevn't observed that on any devices so far, and the old approach // also does sqrt negative value without a check.) If the performance is really // critical, maybe we should just compute the area where temp and x_t are always // valid and drop all these ifs. fragBuilder->codeAppendf("if (temp >= 0) {"); fragBuilder->codeAppendf("x_t = (%csqrt(temp) - %s.x * %s);", sign, p, p0.c_str()); fragBuilder->codeAppendf("}"); } // empty sign is positive char sign = effect.isRadiusIncreasing() ? ' ' : '-'; // "+ 0" is much faster than "+ p1" so we specialize the natively focal case where p1 = 0. fragBuilder->codeAppendf("half %s = %cx_t + %s;", tName, sign, effect.isNativelyFocal() ? "0" : p1.c_str()); if (!effect.isWellBehaved()) { // output will default to transparent black (we simply won't write anything // else to it if invalid, instead of discarding or returning prematurely) fragBuilder->codeAppendf("%s = half4(0.0,0.0,0.0,0.0);", args.fOutputColor); fragBuilder->codeAppendf("if (x_t > 0.0) {"); } if (effect.isSwapped()) { fragBuilder->codeAppendf("%s = 1 - %s;", tName, tName); } this->emitColor(fragBuilder, uniformHandler, args.fShaderCaps, effect, tName, args.fOutputColor, args.fInputColor, args.fTexSamplers); if (!effect.isWellBehaved()) { fragBuilder->codeAppend("};"); } }