void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, U16CPU width, U16CPU height, SkIPoint origin, const GrCaps& caps, SkImageUsageType usage) { const Stretch::Type stretches[] = { Stretch::kNone_Type, // kUntiled_SkImageUsageType Stretch::kNearest_Type, // kTiled_Unfiltered_SkImageUsageType Stretch::kBilerp_Type, // kTiled_Filtered_SkImageUsageType }; const bool isPow2 = SkIsPow2(width) && SkIsPow2(height); const bool needToStretch = !isPow2 && usage != kUntiled_SkImageUsageType && !caps.npotTextureTileSupport(); if (needToStretch) { GrUniqueKey tmpKey; make_unstretched_key(&tmpKey, imageID, width, height, origin); Stretch stretch; stretch.fType = stretches[usage]; stretch.fWidth = SkNextPow2(width); stretch.fHeight = SkNextPow2(height); if (!make_stretched_key(tmpKey, stretch, key)) { goto UNSTRETCHED; } } else { UNSTRETCHED: make_unstretched_key(key, imageID, width, height, origin); } }
bool GrTextureUsageSupported(const GrCaps& caps, int width, int height, SkImageUsageType usage) { if (caps.npotTextureTileSupport()) { return true; } const bool is_pow2 = SkIsPow2(width) && SkIsPow2(height); return is_pow2 || kUntiled_SkImageUsageType == usage; }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>(); GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder(); // emit attributes vsBuilder->emitAttributes(cte); // compute numbers to be hardcoded to convert texture coordinates from int to float SkASSERT(cte.numTextures() == 1); GrTexture* atlas = cte.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); SkScalar recipWidth = 1.0f / atlas->width(); SkScalar recipHeight = 1.0f / atlas->height(); GrGLSLVertToFrag v(kVec2f_GrSLType); pb->addVarying("TextureCoords", &v); vsBuilder->codeAppendf("%s = vec2(%.*f, %.*f) * %s;", v.vsOut(), GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipWidth, GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipHeight, cte.inTextureCoords()->fName); // Setup pass through color if (!cte.colorIgnored()) { if (cte.hasVertexColor()) { pb->addPassThroughAttribute(cte.inColor(), args.fOutputColor); } else { this->setupUniformColor(pb, args.fOutputColor, &fColorUniform); } } // Setup position this->setupPosition(pb, gpArgs, cte.inPosition()->fName); // emit transforms this->emitTransforms(args.fPB, gpArgs->fPositionVar, cte.inPosition()->fName, cte.localMatrix(), args.fTransformsIn, args.fTransformsOut); GrGLSLFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder(); if (cte.maskFormat() == kARGB_GrMaskFormat) { fsBuilder->codeAppendf("%s = ", args.fOutputColor); fsBuilder->appendTextureLookupAndModulate(args.fOutputColor, args.fSamplers[0], v.fsIn(), kVec2f_GrSLType); fsBuilder->codeAppend(";"); fsBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage); } else { fsBuilder->codeAppendf("%s = ", args.fOutputCoverage); fsBuilder->appendTextureLookup(args.fSamplers[0], v.fsIn(), kVec2f_GrSLType); fsBuilder->codeAppend(";"); if (cte.maskFormat() == kA565_GrMaskFormat) { // set alpha to be max of rgb coverage fsBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);", args.fOutputCoverage, args.fOutputCoverage, args.fOutputCoverage, args.fOutputCoverage); } } }
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, FPCoordTransformIter&& transformIter) override { const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>(); if (dfpgp.matrix().hasPerspective() && !fMatrix.cheapEqualTo(dfpgp.matrix())) { fMatrix = dfpgp.matrix(); float matrix[3 * 3]; GrGLSLGetMatrix<3>(matrix, fMatrix); pdman.setMatrix3f(fMatrixUniform, matrix); } SkASSERT(dfpgp.numTextureSamplers() >= 1); GrTexture* atlas = dfpgp.textureSampler(0).peekTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); if (fAtlasSize.fWidth != atlas->width() || fAtlasSize.fHeight != atlas->height()) { fAtlasSize.set(atlas->width(), atlas->height()); pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlas->width(), 1.0f / atlas->height()); } if (dfpgp.matrix().hasPerspective()) { this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); } else { this->setTransformDataHelper(dfpgp.matrix(), pdman, &transformIter); } }
static Stretch get_stretch_type(const GrContext* ctx, int width, int height, const GrTextureParams* params) { if (params && params->isTiled()) { if (!ctx->npotTextureTileSupport() && (!SkIsPow2(width) || !SkIsPow2(height))) { switch(params->filterMode()) { case GrTextureParams::kNone_FilterMode: return kNearest_Stretch; case GrTextureParams::kBilerp_FilterMode: case GrTextureParams::kMipMap_FilterMode: return kBilerp_Stretch; } } } return kNo_Stretch; }
void test_sampleLocations(skiatest::Reporter* reporter, TestSampleLocationsInterface* testInterface, GrContext* ctx) { SkRandom rand; SkAutoTUnref<GrRenderTarget> bottomUps[numTestPatterns]; SkAutoTUnref<GrRenderTarget> topDowns[numTestPatterns]; for (int i = 0; i < numTestPatterns; ++i) { int numSamples = (int)kTestPatterns[i].size(); GrAlwaysAssert(numSamples > 1 && SkIsPow2(numSamples)); bottomUps[i].reset(create_render_target(ctx, kBottomLeft_GrSurfaceOrigin, rand.nextRangeU(1 + numSamples / 2, numSamples))); topDowns[i].reset(create_render_target(ctx, kTopLeft_GrSurfaceOrigin, rand.nextRangeU(1 + numSamples / 2, numSamples))); } // Ensure all sample locations get queried and/or cached properly. GrStencilSettings dummyStencil; for (int repeat = 0; repeat < 2; ++repeat) { for (int i = 0; i < numTestPatterns; ++i) { testInterface->overrideSamplePattern(kTestPatterns[i]); assert_equal(reporter, kTestPatterns[i], topDowns[i]->renderTargetPriv().getMultisampleSpecs(dummyStencil), false); assert_equal(reporter, kTestPatterns[i], bottomUps[i]->renderTargetPriv().getMultisampleSpecs(dummyStencil), true); } } }
void InstancedRendering::Batch::initBatchTracker(const GrXPOverridesForBatch& overrides) { Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command. SkASSERT(draw.fGeometry.isEmpty()); SkASSERT(SkIsPow2(fInfo.fShapeTypes)); SkASSERT(!fIsTracked); if (kRect_ShapeFlag == fInfo.fShapeTypes) { draw.fGeometry = InstanceProcessor::GetIndexRangeForRect(fInfo.fAntialiasMode); } else if (kOval_ShapeFlag == fInfo.fShapeTypes) { draw.fGeometry = InstanceProcessor::GetIndexRangeForOval(fInfo.fAntialiasMode, this->bounds()); } else { draw.fGeometry = InstanceProcessor::GetIndexRangeForRRect(fInfo.fAntialiasMode); } if (!fParams.empty()) { SkASSERT(fInstancedRendering->fParams.count() < (int)kParamsIdx_InfoMask); // TODO: cleaner. this->getSingleInstance().fInfo |= fInstancedRendering->fParams.count(); fInstancedRendering->fParams.push_back_n(fParams.count(), fParams.begin()); } GrColor overrideColor; if (overrides.getOverrideColorIfSet(&overrideColor)) { SkASSERT(State::kRecordingDraws == fInstancedRendering->fState); this->getSingleInstance().fColor = overrideColor; } fInfo.fUsesLocalCoords = overrides.readsLocalCoords(); fInfo.fCannotTweakAlphaForCoverage = !overrides.canTweakAlphaForCoverage(); fInstancedRendering->fTrackedBatches.addToTail(this); fIsTracked = true; }
GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, SkImageUsageType usage) const { const bool is_pow2 = SkIsPow2(this->width()) && SkIsPow2(this->height()); const bool npot_tex_supported = ctx->caps()->npotTextureTileSupport(); if (!is_pow2 && kUntiled_SkImageUsageType != usage && !npot_tex_supported) { // load as bitmap, since the GPU can support tiling a non-pow2 texture // related to skbug.com/4365 SkBitmap bitmap; if (this->getROPixels(&bitmap)) { return GrRefCachedBitmapTexture(ctx, bitmap, usage); } else { return nullptr; } } fTexture->ref(); return fTexture; }
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp, FPCoordTransformIter&& transformIter) override { const GrBitmapTextGeoProc& btgp = gp.cast<GrBitmapTextGeoProc>(); if (btgp.color() != fColor && !btgp.hasVertexColor()) { float c[4]; GrColorToRGBAFloat(btgp.color(), c); pdman.set4fv(fColorUniform, 1, c); fColor = btgp.color(); } const SkISize& atlasSize = btgp.atlasSize(); SkASSERT(SkIsPow2(atlasSize.fWidth) && SkIsPow2(atlasSize.fHeight)); if (fAtlasSize != atlasSize) { pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlasSize.fWidth, 1.0f / atlasSize.fHeight); fAtlasSize = atlasSize; } this->setTransformDataHelper(btgp.localMatrix(), pdman, &transformIter); }
static SkBaseMutex* get_default_mutex() { static int32_t gPixelRefMutexRingIndex; SkASSERT(SkIsPow2(PIXELREF_MUTEX_RING_COUNT)); // atomic_inc might be overkill here. It may be fine if once in a while // we hit a race-condition and two subsequent calls get the same index... int index = sk_atomic_inc(&gPixelRefMutexRingIndex); return &gPixelRefMutexRing[index & (PIXELREF_MUTEX_RING_COUNT - 1)]; }
bool GrGpu::IsACopyNeededForTextureParams(const GrCaps* caps, GrTextureProxy* texProxy, int width, int height, const GrSamplerState& textureParams, GrTextureProducer::CopyParams* copyParams, SkScalar scaleAdjust[2]) { if (texProxy) { // If the texture format itself doesn't support repeat wrap mode or mipmapping (and // those capabilities are required) force a copy. if ((textureParams.isRepeated() && texProxy->texPriv().isClampOnly()) || (GrSamplerState::Filter::kMipMap == textureParams.filter() && texProxy->texPriv().doesNotSupportMipMaps())) { copyParams->fFilter = GrSamplerState::Filter::kNearest; copyParams->fWidth = texProxy->width(); copyParams->fHeight = texProxy->height(); return true; } } if (textureParams.isRepeated() && !caps->npotTextureTileSupport() && (!SkIsPow2(width) || !SkIsPow2(height))) { SkASSERT(scaleAdjust); copyParams->fWidth = GrNextPow2(width); copyParams->fHeight = GrNextPow2(height); SkASSERT(scaleAdjust); scaleAdjust[0] = ((SkScalar) copyParams->fWidth) / width; scaleAdjust[1] = ((SkScalar) copyParams->fHeight) / height; switch (textureParams.filter()) { case GrSamplerState::Filter::kNearest: copyParams->fFilter = GrSamplerState::Filter::kNearest; break; case GrSamplerState::Filter::kBilerp: case GrSamplerState::Filter::kMipMap: // We are only ever scaling up so no reason to ever indicate kMipMap. copyParams->fFilter = GrSamplerState::Filter::kBilerp; break; } return true; } return false; }
bool GrGpu::makeCopyForTextureParams(int width, int height, const GrTextureParams& textureParams, GrTextureProducer::CopyParams* copyParams) const { const GrCaps& caps = *this->caps(); if (textureParams.isTiled() && !caps.npotTextureTileSupport() && (!SkIsPow2(width) || !SkIsPow2(height))) { copyParams->fWidth = GrNextPow2(width); copyParams->fHeight = GrNextPow2(height); switch (textureParams.filterMode()) { case GrTextureParams::kNone_FilterMode: copyParams->fFilter = GrTextureParams::kNone_FilterMode; break; case GrTextureParams::kBilerp_FilterMode: case GrTextureParams::kMipMap_FilterMode: // We are only ever scaling up so no reason to ever indicate kMipMap. copyParams->fFilter = GrTextureParams::kBilerp_FilterMode; break; } return true; } return false; }
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc, const void* srcData, size_t rowBytes) { if (!this->caps()->isConfigTexturable(desc.fConfig)) { return NULL; } if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) && !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { return NULL; } GrTexture *tex = NULL; if (GrPixelConfigIsCompressed(desc.fConfig)) { // We shouldn't be rendering into this SkASSERT((desc.fFlags & kRenderTarget_GrTextureFlagBit) == 0); if (!this->caps()->npotTextureTileSupport() && (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) { return NULL; } this->handleDirtyContext(); tex = this->onCreateCompressedTexture(desc, srcData); } else { this->handleDirtyContext(); tex = this->onCreateTexture(desc, srcData, rowBytes); if (tex && (kRenderTarget_GrTextureFlagBit & desc.fFlags) && !(kNoStencil_GrTextureFlagBit & desc.fFlags)) { SkASSERT(tex->asRenderTarget()); // TODO: defer this and attach dynamically if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) { tex->unref(); return NULL; } } } return tex; }
GrTexture* GrGpu::createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData, size_t rowBytes) { if (!this->caps()->isConfigTexturable(desc.fConfig)) { return NULL; } bool isRT = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag); if (isRT && !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { return NULL; } GrTexture *tex = NULL; if (GrPixelConfigIsCompressed(desc.fConfig)) { // We shouldn't be rendering into this SkASSERT((desc.fFlags & kRenderTarget_GrSurfaceFlag) == 0); if (!this->caps()->npotTextureTileSupport() && (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) { return NULL; } this->handleDirtyContext(); tex = this->onCreateCompressedTexture(desc, budgeted, srcData); } else { this->handleDirtyContext(); tex = this->onCreateTexture(desc, budgeted, srcData, rowBytes); } if (!this->caps()->reuseScratchTextures() && !isRT) { tex->resourcePriv().removeScratchKey(); } if (tex) { fStats.incTextureCreates(); if (srcData) { fStats.incTextureUploads(); } } return tex; }
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, FPCoordTransformIter&& transformIter) override { const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>(); #ifdef SK_GAMMA_APPLY_TO_A8 float distanceAdjust = dfa8gp.getDistanceAdjust(); if (distanceAdjust != fDistanceAdjust) { fDistanceAdjust = distanceAdjust; pdman.set1f(fDistanceAdjustUni, distanceAdjust); } #endif SkASSERT(dfa8gp.numTextureSamplers() >= 1); GrTexture* atlas = dfa8gp.textureSampler(0).peekTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); if (fAtlasSize.fWidth != atlas->width() || fAtlasSize.fHeight != atlas->height()) { fAtlasSize.set(atlas->width(), atlas->height()); pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlas->width(), 1.0f / atlas->height()); } this->setTransformDataHelper(dfa8gp.localMatrix(), pdman, &transformIter); }
static void get_stretch(const GrContext* ctx, int width, int height, const GrTextureParams* params, SkGrStretch* stretch) { stretch->fType = SkGrStretch::kNone_Type; bool doStretch = false; if (params && params->isTiled() && !ctx->caps()->npotTextureTileSupport() && (!SkIsPow2(width) || !SkIsPow2(height))) { doStretch = true; stretch->fWidth = GrNextPow2(SkTMax(width, ctx->caps()->minTextureSize())); stretch->fHeight = GrNextPow2(SkTMax(height, ctx->caps()->minTextureSize())); } else if (width < ctx->caps()->minTextureSize() || height < ctx->caps()->minTextureSize()) { // The small texture issues appear to be with tiling. Hence it seems ok to scale them // up using the GPU. If issues persist we may need to CPU-stretch. doStretch = true; stretch->fWidth = SkTMax(width, ctx->caps()->minTextureSize()); stretch->fHeight = SkTMax(height, ctx->caps()->minTextureSize()); } if (doStretch) { if (params) { switch(params->filterMode()) { case GrTextureParams::kNone_FilterMode: stretch->fType = SkGrStretch::kNearest_Type; break; case GrTextureParams::kBilerp_FilterMode: case GrTextureParams::kMipMap_FilterMode: stretch->fType = SkGrStretch::kBilerp_Type; break; } } else { stretch->fType = SkGrStretch::kBilerp_Type; } } else { stretch->fWidth = -1; stretch->fHeight = -1; stretch->fType = SkGrStretch::kNone_Type; } }
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor, FPCoordTransformIter&& transformIter) override { SkASSERT(fDistanceAdjustUni.isValid()); const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>(); GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.getDistanceAdjust(); if (wa != fDistanceAdjust) { pdman.set3f(fDistanceAdjustUni, wa.fR, wa.fG, wa.fB); fDistanceAdjust = wa; } SkASSERT(dflcd.numTextureSamplers() >= 1); GrTexture* atlas = dflcd.textureSampler(0).peekTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); if (fAtlasSize.fWidth != atlas->width() || fAtlasSize.fHeight != atlas->height()) { fAtlasSize.set(atlas->width(), atlas->height()); pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlas->width(), 1.0f / atlas->height()); } this->setTransformDataHelper(dflcd.localMatrix(), pdman, &transformIter); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color if (!dfTexEffect.colorIgnored()) { varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // set up varyings bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); GrGLSLVertToFrag recipScale(kFloat_GrSLType); GrGLSLVertToFrag uv(kVec2f_GrSLType); varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from float to int SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); GrGLSLVertToFrag st(kVec2f_GrSLType); varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(), atlas->width(), atlas->height(), dfTexEffect.inTextureCoords()->fName); // add frag shader code SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); // create LCD offset adjusted by inverse of transform // Use highp to work around aliasing issues fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); fragBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); SkScalar lcdDelta = 1.0f / (3.0f * atlas->width()); if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { fragBuilder->codeAppendf("float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } else { fragBuilder->codeAppendf("float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } if (isUniformScale) { fragBuilder->codeAppendf("float st_grad_len = abs(dFdy(%s.y));", st.fsIn()); fragBuilder->codeAppend("vec2 offset = vec2(st_grad_len*delta, 0.0);"); } else if (isSimilarity) { // For a similarity matrix with rotation, the gradient will not be aligned // with the texel coordinate axes, so we need to calculate it. // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to // get the gradient in the x direction. fragBuilder->codeAppendf("vec2 st_grad = dFdy(%s);", st.fsIn()); fragBuilder->codeAppend("float st_grad_len = length(st_grad);"); fragBuilder->codeAppend("vec2 offset = delta*vec2(st_grad.y, -st_grad.x);"); } else { fragBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn()); fragBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fragBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fragBuilder->codeAppend("vec2 offset = delta*Jdx;"); } // green is distance to uv center fragBuilder->codeAppend("\tvec4 texColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppend("\tvec3 distance;\n"); fragBuilder->codeAppend("\tdistance.y = texColor.r;\n"); // red is distance to left offset fragBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n"); fragBuilder->codeAppend("\ttexColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppend("\tdistance.x = texColor.r;\n"); // blue is distance to right offset fragBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); fragBuilder->codeAppend("\ttexColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppend("\tdistance.z = texColor.r;\n"); fragBuilder->codeAppend("\tdistance = " "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));"); // adjust width based on gamma const char* distanceAdjustUniName = nullptr; fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec3f_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); // To be strictly correct, we should compute the anti-aliasing factor separately // for each color component. However, this is only important when using perspective // transformations, and even then using a single factor seems like a reasonable // trade-off between quality and speed. fragBuilder->codeAppend("float afwidth;"); if (isSimilarity) { // For similarity transform (uniform scale-only is a subset of this), we adjust for the // effect of the transformation on the distance by using the length of the gradient of // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel // space to pixel space. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fragBuilder->codeAppend( "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);"); // set alpha to be max of rgb coverage fragBuilder->codeAppend("val.a = max(max(val.r, val.g), val.b);"); fragBuilder->codeAppendf("%s = val;", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldA8TextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldA8TextGeoProc>(); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); #ifdef SK_GAMMA_APPLY_TO_A8 // adjust based on gamma const char* distanceAdjustUniName = nullptr; // width, height, 1/(3*width) fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); #endif // Setup pass through color if (!dfTexEffect.colorIgnored()) { varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // add varyings GrGLSLVertToFrag recipScale(kFloat_GrSLType); GrGLSLVertToFrag uv(kVec2f_GrSLType); bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from float to int SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); GrGLSLVertToFrag st(kVec2f_GrSLType); varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(), atlas->width(), atlas->height(), dfTexEffect.inTextureCoords()->fName); // Use highp to work around aliasing issues fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); fragBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fragBuilder->codeAppend("\tfloat texColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fragBuilder->codeAppend(".r;\n"); fragBuilder->codeAppend("\tfloat distance = " SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); #ifdef SK_GAMMA_APPLY_TO_A8 // adjust width based on gamma fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); #endif fragBuilder->codeAppend("float afwidth;"); if (isUniformScale) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the t coordinate in the y direction. // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. // We use the y gradient because there is a bug in the Mali 400 in the x direction. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));", st.fsIn()); } else if (isSimilarity) { // For similarity transform, we adjust the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // We use the y gradient because there is a bug in the Mali 400 in the x direction. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppendf("float st_grad_len = length(dFdy(%s));", st.fsIn()); fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn()); fragBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn()); fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, sk_tools::PictureBenchmark* benchmark) { const char* argv0 = argv[0]; char* const* stop = argv + argc; int repeats = DEFAULT_REPEATS; sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL); // Create a string to show our current settings. // TODO: Make it prettier. Currently it just repeats the command line. SkString commandLine("bench_pictures:"); for (int i = 1; i < argc; i++) { commandLine.appendf(" %s", *(argv+i)); } commandLine.append("\n"); bool usePipe = false; int numThreads = 1; bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; int gridWidth = 0; int gridHeight = 0; bool isPowerOf2Mode = false; bool isCopyMode = false; const char* xTilesString = NULL; const char* yTilesString = NULL; const char* mode = NULL; bool gridSupported = false; sk_tools::PictureRenderer::BBoxHierarchyType bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount]; sk_bzero(drawFilters, sizeof(drawFilters)); SkISize viewport; viewport.setEmpty(); for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--repeat")) { ++argv; if (argv < stop) { repeats = atoi(*argv); if (repeats < 1) { gLogger.logError("--repeat must be given a value > 0\n"); PRINT_USAGE_AND_EXIT; } } else { gLogger.logError("Missing arg for --repeat\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--pipe")) { usePipe = true; } else if (0 == strcmp(*argv, "--logFile")) { argv++; if (argv < stop) { if (!gLogger.SetLogFile(*argv)) { SkString str; str.printf("Could not open %s for writing.", *argv); gLogger.logError(str); usage(argv0); // TODO(borenet): We're disabling this for now, due to // write-protected Android devices. The very short-term // solution is to ignore the fact that we have no log file. //exit(-1); } } else { gLogger.logError("Missing arg for --logFile\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--multi")) { ++argv; if (argv >= stop) { gLogger.logError("Missing arg for --multi\n"); PRINT_USAGE_AND_EXIT; } numThreads = atoi(*argv); if (numThreads < 2) { gLogger.logError("Number of threads must be at least 2.\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--bbh")) { ++argv; if (argv >= stop) { gLogger.logError("Missing value for --bbh\n"); PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "none")) { bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; } else if (0 == strcmp(*argv, "rtree")) { bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType; } else if (0 == strcmp(*argv, "grid")) { bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType; ++argv; if (argv >= stop) { gLogger.logError("Missing width for --bbh grid\n"); PRINT_USAGE_AND_EXIT; } gridWidth = atoi(*argv); ++argv; if (argv >= stop) { gLogger.logError("Missing height for --bbh grid\n"); PRINT_USAGE_AND_EXIT; } gridHeight = atoi(*argv); } else { SkString err; err.printf("%s is not a valid value for --bbhType\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--mode")) { if (renderer.get() != NULL) { SkDebugf("Cannot combine modes.\n"); PRINT_USAGE_AND_EXIT; } ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --mode\n"); PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "record")) { renderer.reset(SkNEW(sk_tools::RecordPictureRenderer)); gridSupported = true; } else if (0 == strcmp(*argv, "clone")) { renderer.reset(sk_tools::CreatePictureCloneRenderer()); } else if (0 == strcmp(*argv, "simple")) { renderer.reset(SkNEW(sk_tools::SimplePictureRenderer)); } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile")) || 0 == strcmp(*argv, "copyTile")) { useTiles = true; mode = *argv; if (0 == strcmp(*argv, "pow2tile")) { isPowerOf2Mode = true; } else if (0 == strcmp(*argv, "copyTile")) { isCopyMode = true; } else { gridSupported = true; } ++argv; if (argv >= stop) { SkString err; err.printf("Missing width for --mode %s\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } widthString = *argv; ++argv; if (argv >= stop) { SkString err; err.appendf("Missing height for --mode %s\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } heightString = *argv; } else if (0 == strcmp(*argv, "playbackCreation")) { renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer)); gridSupported = true; } else if (0 == strcmp(*argv, "gatherPixelRefs")) { renderer.reset(sk_tools::CreateGatherPixelRefsRenderer()); } else { SkString err; err.printf("%s is not a valid mode for --mode\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--viewport")) { ++argv; if (argv >= stop) { gLogger.logError("Missing width for --viewport\n"); PRINT_USAGE_AND_EXIT; } viewport.fWidth = atoi(*argv); ++argv; if (argv >= stop) { gLogger.logError("Missing height for --viewport\n"); PRINT_USAGE_AND_EXIT; } viewport.fHeight = atoi(*argv); } else if (0 == strcmp(*argv, "--tiles")) { ++argv; if (argv >= stop) { gLogger.logError("Missing x for --tiles\n"); PRINT_USAGE_AND_EXIT; } xTilesString = *argv; ++argv; if (argv >= stop) { gLogger.logError("Missing y for --tiles\n"); PRINT_USAGE_AND_EXIT; } yTilesString = *argv; } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --device\n"); PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "bitmap")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(*argv, "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; } #endif else { SkString err; err.printf("%s is not a valid mode for --device\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--timers")) { ++argv; if (argv < stop) { bool timerWall = false; bool truncatedTimerWall = false; bool timerCpu = false; bool truncatedTimerCpu = false; bool timerGpu = false; for (char* t = *argv; *t; ++t) { switch (*t) { case 'w': timerWall = true; break; case 'c': timerCpu = true; break; case 'W': truncatedTimerWall = true; break; case 'C': truncatedTimerCpu = true; break; case 'g': timerGpu = true; break; default: { break; } } } benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu, timerGpu); } else { gLogger.logError("Missing arg for --timers\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--timeIndividualTiles")) { benchmark->setTimeIndividualTiles(true); } else if (0 == strcmp(*argv, "--min")) { benchmark->setPrintMin(true); } else if (0 == strcmp(*argv, "--logPerIter")) { ++argv; if (argv < stop) { bool log = atoi(*argv) != 0; benchmark->setLogPerIter(log); } else { gLogger.logError("Missing arg for --logPerIter\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--filter")) { ++argv; if (argv < stop) { const char* colon = strchr(*argv, ':'); if (colon) { int type = -1; size_t typeLen = colon - *argv; for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) { if (typeLen == strlen(gFilterTypes[tIndex]) && !strncmp(*argv, gFilterTypes[tIndex], typeLen)) { type = tIndex; break; } } if (type < 0) { SkString err; err.printf("Unknown type for --filter %s\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } int flag = -1; size_t flagLen = strlen(*argv) - typeLen - 1; for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) { if (flagLen == strlen(gFilterFlags[fIndex]) && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) { flag = 1 << fIndex; break; } } if (flag < 0) { SkString err; err.printf("Unknown flag for --filter %s\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) { if (type != SkDrawFilter::kTypeCount && index != type) { continue; } drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags) (drawFilters[index] | flag); } } else { SkString err; err.printf("Unknown arg for --filter %s : missing colon\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else { gLogger.logError("Missing arg for --filter\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) { PRINT_USAGE_AND_EXIT; } else { inputs->push_back(SkString(*argv)); } } if (numThreads > 1 && !useTiles) { gLogger.logError("Multithreaded drawing requires tiled rendering.\n"); PRINT_USAGE_AND_EXIT; } if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { gLogger.logError("--pipe and --bbh cannot be used together\n"); PRINT_USAGE_AND_EXIT; } if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType && !gridSupported) { gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n"); PRINT_USAGE_AND_EXIT; } if (useTiles) { SkASSERT(NULL == renderer); sk_tools::TiledPictureRenderer* tiledRenderer; if (isCopyMode) { int x, y; if (xTilesString != NULL) { SkASSERT(yTilesString != NULL); x = atoi(xTilesString); y = atoi(yTilesString); if (x <= 0 || y <= 0) { gLogger.logError("--tiles must be given values > 0\n"); PRINT_USAGE_AND_EXIT; } } else { x = y = 4; } tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)); if (benchmark->timeIndividualTiles()) { gLogger.logError("timeIndividualTiles is not compatible with copyTile\n"); PRINT_USAGE_AND_EXIT; } } else if (numThreads > 1) { tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads)); } else { tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); } if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { tiledRenderer->unref(); SkString err; err.printf("-mode %s must be given a width" " value that is a power of two\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { if (isCopyMode) { tiledRenderer->unref(); SkString err; err.printf("--mode %s does not support percentages.\n", mode); gLogger.logError(err.c_str()); PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileWidthPercentage(atof(widthString)); if (!(tiledRenderer->getTileWidthPercentage() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a width percentage > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else { tiledRenderer->setTileWidth(atoi(widthString)); if (!(tiledRenderer->getTileWidth() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a width > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } if (sk_tools::is_percentage(heightString)) { if (isCopyMode) { tiledRenderer->unref(); SkString err; err.printf("--mode %s does not support percentages.\n", mode); gLogger.logError(err.c_str()); PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileHeightPercentage(atof(heightString)); if (!(tiledRenderer->getTileHeightPercentage() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a height percentage > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else { tiledRenderer->setTileHeight(atoi(heightString)); if (!(tiledRenderer->getTileHeight() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a height > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } if (numThreads > 1) { #if SK_SUPPORT_GPU if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { tiledRenderer->unref(); gLogger.logError("GPU not compatible with multithreaded tiling.\n"); PRINT_USAGE_AND_EXIT; } #endif } renderer.reset(tiledRenderer); if (usePipe) { gLogger.logError("Pipe rendering is currently not compatible with tiling.\n" "Turning off pipe.\n"); } } else { if (benchmark->timeIndividualTiles()) { gLogger.logError("timeIndividualTiles requires tiled rendering.\n"); PRINT_USAGE_AND_EXIT; } if (usePipe) { if (renderer.get() != NULL) { gLogger.logError("Pipe is incompatible with other modes.\n"); PRINT_USAGE_AND_EXIT; } renderer.reset(SkNEW(sk_tools::PipePictureRenderer)); } } if (inputs->count() < 1) { PRINT_USAGE_AND_EXIT; } if (NULL == renderer) { renderer.reset(SkNEW(sk_tools::SimplePictureRenderer)); } renderer->setBBoxHierarchyType(bbhType); renderer->setDrawFilters(drawFilters, filtersName(drawFilters)); renderer->setGridSize(gridWidth, gridHeight); renderer->setViewport(viewport); benchmark->setRenderer(renderer); benchmark->setRepeats(repeats); benchmark->setDeviceType(deviceType); benchmark->setLogger(&gLogger); // Report current settings: gLogger.logProgress(commandLine); }
GrTexture* GrGpu::createTexture(const GrSurfaceDesc& origDesc, bool budgeted, const void* srcData, size_t rowBytes) { GrSurfaceDesc desc = origDesc; if (!this->caps()->isConfigTexturable(desc.fConfig)) { return nullptr; } bool isRT = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag); if (isRT && !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { return nullptr; } // We currently do not support multisampled textures if (!isRT && desc.fSampleCnt > 0) { return nullptr; } GrTexture *tex = nullptr; if (isRT) { int maxRTSize = this->caps()->maxRenderTargetSize(); if (desc.fWidth > maxRTSize || desc.fHeight > maxRTSize) { return nullptr; } } else { int maxSize = this->caps()->maxTextureSize(); if (desc.fWidth > maxSize || desc.fHeight > maxSize) { return nullptr; } } GrGpuResource::LifeCycle lifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle : GrGpuResource::kUncached_LifeCycle; desc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount()); // Attempt to catch un- or wrongly initialized sample counts; SkASSERT(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64); desc.fOrigin = resolve_origin(desc.fOrigin, isRT); if (GrPixelConfigIsCompressed(desc.fConfig)) { // We shouldn't be rendering into this SkASSERT(!isRT); SkASSERT(0 == desc.fSampleCnt); if (!this->caps()->npotTextureTileSupport() && (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) { return nullptr; } this->handleDirtyContext(); tex = this->onCreateCompressedTexture(desc, lifeCycle, srcData); } else { this->handleDirtyContext(); tex = this->onCreateTexture(desc, lifeCycle, srcData, rowBytes); } if (!this->caps()->reuseScratchTextures() && !isRT) { tex->resourcePriv().removeScratchKey(); } if (tex) { fStats.incTextureCreates(); if (srcData) { fStats.incTextureUploads(); } } return tex; }
sk_sp<GrDrawContext> GaussianBlur(GrContext* context, GrTexture* origSrc, sk_sp<SkColorSpace> colorSpace, const SkIRect& dstBounds, const SkIRect* srcBounds, float sigmaX, float sigmaY, SkBackingFit fit) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; int maxTextureSize = context->caps()->maxTextureSize(); sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); SkASSERT(sigmaX || sigmaY); SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y()); SkIRect localDstBounds = SkIRect::MakeWH(dstBounds.width(), dstBounds.height()); SkIRect localSrcBounds; SkIRect srcRect; if (srcBounds) { srcRect = localSrcBounds = *srcBounds; srcRect.offset(srcOffset); srcBounds = &localSrcBounds; } else { srcRect = localDstBounds; } scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); scale_irect(&srcRect, scaleFactorX, scaleFactorY); // setup new clip GrFixedClip clip(localDstBounds); sk_sp<GrTexture> srcTexture(sk_ref_sp(origSrc)); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kSRGBA_8888_GrPixelConfig == srcTexture->config() || kSBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_half_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); const int width = dstBounds.width(); const int height = dstBounds.height(); const GrPixelConfig config = srcTexture->config(); sk_sp<GrDrawContext> dstDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!dstDrawContext) { return nullptr; } // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches if (sigmaX > 0.0f && sigmaY > 0.0f && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY)); convolve_gaussian_2d(dstDrawContext.get(), clip, localDstBounds, srcOffset, srcTexture.get(), radiusX, radiusY, sigmaX, sigmaY, srcBounds); return dstDrawContext; } sk_sp<GrDrawContext> tmpDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!tmpDrawContext) { return nullptr; } sk_sp<GrDrawContext> srcDrawContext; SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY)); for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; paint.setGammaCorrect(dstDrawContext->isGammaCorrect()); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkIRect dstRect(srcRect); if (srcBounds && i == 1) { SkRect domain; matrix.mapRect(&domain, SkRect::Make(*srcBounds)); domain.inset((i < scaleFactorX) ? SK_ScalarHalf / srcTexture->width() : 0.0f, (i < scaleFactorY) ? SK_ScalarHalf / srcTexture->height() : 0.0f); sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make( srcTexture.get(), nullptr, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorFragmentProcessor(std::move(fp)); srcRect.offset(-srcOffset); srcOffset.set(0, 0); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture.get(), nullptr, matrix, params); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY); dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), SkRect::Make(srcRect)); srcDrawContext = dstDrawContext; srcRect = dstRect; srcTexture = srcDrawContext->asTexture(); dstDrawContext.swap(tmpDrawContext); localSrcBounds = srcRect; } srcRect = localDstBounds; scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); if (sigmaX > 0.0f) { if (scaleFactorX > 1) { SkASSERT(srcDrawContext); // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, radiusX, srcRect.height()); srcDrawContext->clear(&clearRect, 0x0, false); } convolve_gaussian(dstDrawContext.get(), clip, srcRect, srcTexture.get(), Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, srcBounds, srcOffset); srcDrawContext = dstDrawContext; srcTexture = srcDrawContext->asTexture(); srcRect.offsetTo(0, 0); dstDrawContext.swap(tmpDrawContext); localSrcBounds = srcRect; srcOffset.set(0, 0); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { SkASSERT(srcDrawContext); // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width(), radiusY); srcDrawContext->clear(&clearRect, 0x0, false); } convolve_gaussian(dstDrawContext.get(), clip, srcRect, srcTexture.get(), Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, srcBounds, srcOffset); srcDrawContext = dstDrawContext; srcRect.offsetTo(0, 0); dstDrawContext.swap(tmpDrawContext); } SkASSERT(srcDrawContext); srcTexture = nullptr; // we don't use this from here on out if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear upsampling. clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1); srcDrawContext->clear(&clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.height()); srcDrawContext->clear(&clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcDrawContext->width(), srcDrawContext->height()); GrPaint paint; paint.setGammaCorrect(dstDrawContext->isGammaCorrect()); // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); sk_sp<GrTexture> tex(srcDrawContext->asTexture()); paint.addColorTextureProcessor(tex.get(), nullptr, matrix, params); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkIRect dstRect(srcRect); scale_irect(&dstRect, scaleFactorX, scaleFactorY); dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), SkRect::Make(srcRect)); srcDrawContext = dstDrawContext; srcRect = dstRect; dstDrawContext.swap(tmpDrawContext); } return srcDrawContext; }
sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) { error.reset(); if (FLAGS_multi <= 0) { error.printf("--multi must be > 0, was %i", FLAGS_multi); return NULL; } bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; bool isPowerOf2Mode = false; bool isCopyMode = false; const char* mode = NULL; bool gridSupported = false; SkAutoTUnref<sk_tools::PictureRenderer> renderer; if (FLAGS_mode.count() >= 1) { mode = FLAGS_mode[0]; if (0 == strcmp(mode, "record")) { renderer.reset(SkNEW(sk_tools::RecordPictureRenderer)); gridSupported = true; // undocumented } else if (0 == strcmp(mode, "clone")) { renderer.reset(sk_tools::CreatePictureCloneRenderer()); } else if (0 == strcmp(mode, "tile") || 0 == strcmp(mode, "pow2tile") || 0 == strcmp(mode, "copyTile")) { useTiles = true; if (0 == strcmp(mode, "pow2tile")) { isPowerOf2Mode = true; } else if (0 == strcmp(mode, "copyTile")) { isCopyMode = true; } else { gridSupported = true; } if (FLAGS_mode.count() < 2) { error.printf("Missing width for --mode %s\n", mode); return NULL; } widthString = FLAGS_mode[1]; if (FLAGS_mode.count() < 3) { error.printf("Missing height for --mode %s\n", mode); return NULL; } heightString = FLAGS_mode[2]; } else if (0 == strcmp(mode, "playbackCreation") && kBench_PictureTool == tool) { renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer)); gridSupported = true; // undocumented } else if (0 == strcmp(mode, "gatherPixelRefs") && kBench_PictureTool == tool) { renderer.reset(sk_tools::CreateGatherPixelRefsRenderer()); } else if (0 == strcmp(mode, "rerecord") && kRender_PictureTool == tool) { renderer.reset(SkNEW(sk_tools::RecordPictureRenderer)); // Allow 'mode' to be set to 'simple', but do not create a renderer, so we can // ensure that pipe does not override a mode besides simple. The renderer will // be created below. } else if (0 != strcmp(mode, "simple")) { error.printf("%s is not a valid mode for --mode\n", mode); return NULL; } } if (useTiles) { SkASSERT(NULL == renderer); SkAutoTUnref<sk_tools::TiledPictureRenderer> tiledRenderer; if (isCopyMode) { int xTiles = -1; int yTiles = -1; if (FLAGS_tiles.count() > 0) { if (FLAGS_tiles.count() != 2) { error.printf("--tiles requires an x value and a y value.\n"); return NULL; } xTiles = atoi(FLAGS_tiles[0]); yTiles = atoi(FLAGS_tiles[1]); } int x, y; if (xTiles != -1 && yTiles != -1) { x = xTiles; y = yTiles; if (x <= 0 || y <= 0) { error.printf("--tiles must be given values > 0\n"); return NULL; } } else { x = y = 4; } tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y))); } else if (FLAGS_multi > 1) { tiledRenderer.reset(SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (FLAGS_multi))); } else { tiledRenderer.reset(SkNEW(sk_tools::TiledPictureRenderer)); } if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { SkString err; error.printf("-mode %s must be given a width" " value that is a power of two\n", mode); return NULL; } tiledRenderer->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { if (isCopyMode) { error.printf("--mode %s does not support percentages.\n", mode); return NULL; } tiledRenderer->setTileWidthPercentage(atof(widthString)); if (!(tiledRenderer->getTileWidthPercentage() > 0)) { error.printf("--mode %s must be given a width percentage > 0\n", mode); return NULL; } } else { tiledRenderer->setTileWidth(atoi(widthString)); if (!(tiledRenderer->getTileWidth() > 0)) { error.printf("--mode %s must be given a width > 0\n", mode); return NULL; } } if (sk_tools::is_percentage(heightString)) { if (isCopyMode) { error.printf("--mode %s does not support percentages.\n", mode); return NULL; } tiledRenderer->setTileHeightPercentage(atof(heightString)); if (!(tiledRenderer->getTileHeightPercentage() > 0)) { error.printf("--mode %s must be given a height percentage > 0\n", mode); return NULL; } } else { tiledRenderer->setTileHeight(atoi(heightString)); if (!(tiledRenderer->getTileHeight() > 0)) { SkString err; error.printf("--mode %s must be given a height > 0\n", mode); return NULL; } } renderer.reset(tiledRenderer.detach()); if (FLAGS_pipe) { error.printf("Pipe rendering is currently not compatible with tiling.\n" "Turning off pipe.\n"); } } else { // useTiles if (FLAGS_multi > 1) { error.printf("Multithreaded drawing requires tiled rendering.\n"); return NULL; } if (FLAGS_pipe) { if (renderer != NULL) { error.printf("Pipe is incompatible with other modes.\n"); return NULL; } renderer.reset(SkNEW(sk_tools::PipePictureRenderer)); } } if (NULL == renderer) { renderer.reset(SkNEW(sk_tools::SimplePictureRenderer)); } if (FLAGS_viewport.count() > 0) { if (FLAGS_viewport.count() != 2) { error.printf("--viewport requires a width and a height.\n"); return NULL; } SkISize viewport; viewport.fWidth = atoi(FLAGS_viewport[0]); viewport.fHeight = atoi(FLAGS_viewport[1]); renderer->setViewport(viewport); } sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; #if SK_SUPPORT_GPU int sampleCount = 0; #endif if (FLAGS_config.count() > 0) { if (0 == strcmp(FLAGS_config[0], "8888")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(FLAGS_config[0], "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; if (FLAGS_multi > 1) { error.printf("GPU not compatible with multithreaded tiling.\n"); return NULL; } } else if (0 == strcmp(FLAGS_config[0], "msaa4")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; if (FLAGS_multi > 1) { error.printf("GPU not compatible with multithreaded tiling.\n"); return NULL; } sampleCount = 4; } else if (0 == strcmp(FLAGS_config[0], "msaa16")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; if (FLAGS_multi > 1) { error.printf("GPU not compatible with multithreaded tiling.\n"); return NULL; } sampleCount = 16; } else if (0 == strcmp(FLAGS_config[0], "nvprmsaa4")) { deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType; if (FLAGS_multi > 1) { error.printf("GPU not compatible with multithreaded tiling.\n"); return NULL; } sampleCount = 4; } else if (0 == strcmp(FLAGS_config[0], "nvprmsaa16")) { deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType; if (FLAGS_multi > 1) { error.printf("GPU not compatible with multithreaded tiling.\n"); return NULL; } sampleCount = 16; } #if SK_ANGLE else if (0 == strcmp(FLAGS_config[0], "angle")) { deviceType = sk_tools::PictureRenderer::kAngle_DeviceType; if (FLAGS_multi > 1) { error.printf("Angle not compatible with multithreaded tiling.\n"); return NULL; } } #endif #if SK_MESA else if (0 == strcmp(FLAGS_config[0], "mesa")) { deviceType = sk_tools::PictureRenderer::kMesa_DeviceType; if (FLAGS_multi > 1) { error.printf("Mesa not compatible with multithreaded tiling.\n"); return NULL; } } #endif #endif else { error.printf("%s is not a valid mode for --config\n", FLAGS_config[0]); return NULL; } renderer->setDeviceType(deviceType); #if SK_SUPPORT_GPU renderer->setSampleCount(sampleCount); #endif } sk_tools::PictureRenderer::BBoxHierarchyType bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; if (FLAGS_bbh.count() > 0) { const char* type = FLAGS_bbh[0]; if (0 == strcmp(type, "none")) { bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; } else if (0 == strcmp(type, "quadtree")) { bbhType = sk_tools::PictureRenderer::kQuadTree_BBoxHierarchyType; } else if (0 == strcmp(type, "rtree")) { bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType; } else if (0 == strcmp(type, "grid")) { if (!gridSupported) { error.printf("'--bbh grid' is not compatible with --mode=%s.\n", mode); return NULL; } bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType; if (FLAGS_bbh.count() != 3) { error.printf("--bbh grid requires a width and a height.\n"); return NULL; } int gridWidth = atoi(FLAGS_bbh[1]); int gridHeight = atoi(FLAGS_bbh[2]); renderer->setGridSize(gridWidth, gridHeight); } else { error.printf("%s is not a valid value for --bbhType\n", type); return NULL; } if (FLAGS_pipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { error.printf("--pipe and --bbh cannot be used together\n"); return NULL; } } renderer->setBBoxHierarchyType(bbhType); renderer->setScaleFactor(SkDoubleToScalar(FLAGS_scale)); return renderer.detach(); }
sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) { error.reset(); bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; bool isPowerOf2Mode = false; bool isCopyMode = false; const char* mode = NULL; #if SK_SUPPORT_GPU GrContextOptions grContextOpts; grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks; #define RENDERER_ARGS (grContextOpts) #else #define RENDERER_ARGS () #endif SkAutoTUnref<sk_tools::PictureRenderer> renderer; if (FLAGS_mode.count() >= 1) { mode = FLAGS_mode[0]; if (0 == strcmp(mode, "record")) { renderer.reset(SkNEW_ARGS(sk_tools::RecordPictureRenderer, RENDERER_ARGS)); } else if (0 == strcmp(mode, "tile") || 0 == strcmp(mode, "pow2tile") || 0 == strcmp(mode, "copyTile")) { useTiles = true; if (0 == strcmp(mode, "pow2tile")) { isPowerOf2Mode = true; } else if (0 == strcmp(mode, "copyTile")) { isCopyMode = true; } if (FLAGS_mode.count() < 2) { error.printf("Missing width for --mode %s\n", mode); return NULL; } widthString = FLAGS_mode[1]; if (FLAGS_mode.count() < 3) { error.printf("Missing height for --mode %s\n", mode); return NULL; } heightString = FLAGS_mode[2]; } else if (0 == strcmp(mode, "playbackCreation") && kBench_PictureTool == tool) { renderer.reset(SkNEW_ARGS(sk_tools::PlaybackCreationRenderer, RENDERER_ARGS)); // undocumented } else if (0 == strcmp(mode, "rerecord") && kRender_PictureTool == tool) { renderer.reset(SkNEW_ARGS(sk_tools::RecordPictureRenderer, RENDERER_ARGS)); } else if (0 == strcmp(mode, "simple")) { // Allow 'mode' to be set to 'simple', but do not create a renderer, so we can // ensure that pipe does not override a mode besides simple. The renderer will // be created below. } else { error.printf("%s is not a valid mode for --mode\n", mode); return NULL; } } if (useTiles) { SkASSERT(NULL == renderer); SkAutoTUnref<sk_tools::TiledPictureRenderer> tiledRenderer; if (isCopyMode) { int xTiles = -1; int yTiles = -1; if (FLAGS_tiles.count() > 0) { if (FLAGS_tiles.count() != 2) { error.printf("--tiles requires an x value and a y value.\n"); return NULL; } xTiles = atoi(FLAGS_tiles[0]); yTiles = atoi(FLAGS_tiles[1]); } int x, y; if (xTiles != -1 && yTiles != -1) { x = xTiles; y = yTiles; if (x <= 0 || y <= 0) { error.printf("--tiles must be given values > 0\n"); return NULL; } } else { x = y = 4; } #if SK_SUPPORT_GPU tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (grContextOpts, x, y))); #else tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y))); #endif } else { tiledRenderer.reset(SkNEW_ARGS(sk_tools::TiledPictureRenderer, RENDERER_ARGS)); } if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { SkString err; error.printf("-mode %s must be given a width" " value that is a power of two\n", mode); return NULL; } tiledRenderer->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { if (isCopyMode) { error.printf("--mode %s does not support percentages.\n", mode); return NULL; } tiledRenderer->setTileWidthPercentage(atof(widthString)); if (!(tiledRenderer->getTileWidthPercentage() > 0)) { error.printf("--mode %s must be given a width percentage > 0\n", mode); return NULL; } } else { tiledRenderer->setTileWidth(atoi(widthString)); if (!(tiledRenderer->getTileWidth() > 0)) { error.printf("--mode %s must be given a width > 0\n", mode); return NULL; } } if (sk_tools::is_percentage(heightString)) { if (isCopyMode) { error.printf("--mode %s does not support percentages.\n", mode); return NULL; } tiledRenderer->setTileHeightPercentage(atof(heightString)); if (!(tiledRenderer->getTileHeightPercentage() > 0)) { error.printf("--mode %s must be given a height percentage > 0\n", mode); return NULL; } } else { tiledRenderer->setTileHeight(atoi(heightString)); if (!(tiledRenderer->getTileHeight() > 0)) { SkString err; error.printf("--mode %s must be given a height > 0\n", mode); return NULL; } } renderer.reset(tiledRenderer.detach()); if (FLAGS_pipe) { error.printf("Pipe rendering is currently not compatible with tiling.\n" "Turning off pipe.\n"); } } else { // useTiles if (FLAGS_pipe) { if (renderer != NULL) { error.printf("Pipe is incompatible with other modes.\n"); return NULL; } renderer.reset(SkNEW_ARGS(sk_tools::PipePictureRenderer, RENDERER_ARGS)); } } if (NULL == renderer) { renderer.reset(SkNEW_ARGS(sk_tools::SimplePictureRenderer, RENDERER_ARGS)); } if (FLAGS_viewport.count() > 0) { if (FLAGS_viewport.count() != 2) { error.printf("--viewport requires a width and a height.\n"); return NULL; } SkISize viewport; viewport.fWidth = atoi(FLAGS_viewport[0]); viewport.fHeight = atoi(FLAGS_viewport[1]); renderer->setViewport(viewport); } sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; #if SK_SUPPORT_GPU GrGLStandard gpuAPI = kNone_GrGLStandard; if (1 == FLAGS_gpuAPI.count()) { if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) { gpuAPI = kGL_GrGLStandard; } else if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) { gpuAPI = kGLES_GrGLStandard; } else { error.printf("--gpuAPI invalid api value.\n"); return NULL; } } else if (FLAGS_gpuAPI.count() > 1) { error.printf("--gpuAPI invalid api value.\n"); return NULL; } int sampleCount = 0; bool useDFText = false; #endif if (FLAGS_config.count() > 0) { if (0 == strcmp(FLAGS_config[0], "8888")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(FLAGS_config[0], "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; } else if (0 == strcmp(FLAGS_config[0], "msaa4")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; sampleCount = 4; } else if (0 == strcmp(FLAGS_config[0], "msaa16")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; sampleCount = 16; } else if (0 == strcmp(FLAGS_config[0], "nvprmsaa4")) { deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType; sampleCount = 4; } else if (0 == strcmp(FLAGS_config[0], "nvprmsaa16")) { deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType; sampleCount = 16; } else if (0 == strcmp(FLAGS_config[0], "gpudft")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; useDFText = true; } #if SK_ANGLE else if (0 == strcmp(FLAGS_config[0], "angle")) { deviceType = sk_tools::PictureRenderer::kAngle_DeviceType; } #endif #if SK_MESA else if (0 == strcmp(FLAGS_config[0], "mesa")) { deviceType = sk_tools::PictureRenderer::kMesa_DeviceType; } #endif #endif else { error.printf("%s is not a valid mode for --config\n", FLAGS_config[0]); return NULL; } #if SK_SUPPORT_GPU if (!renderer->setDeviceType(deviceType, gpuAPI)) { #else if (!renderer->setDeviceType(deviceType)) { #endif error.printf("Could not create backend for --config %s\n", FLAGS_config[0]); return NULL; } #if SK_SUPPORT_GPU renderer->setSampleCount(sampleCount); renderer->setUseDFText(useDFText); #endif } sk_tools::PictureRenderer::BBoxHierarchyType bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; if (FLAGS_bbh.count() > 0) { const char* type = FLAGS_bbh[0]; if (0 == strcmp(type, "none")) { bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; } else if (0 == strcmp(type, "rtree")) { bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType; } else { error.printf("%s is not a valid value for --bbhType\n", type); return NULL; } if (FLAGS_pipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { error.printf("--pipe and --bbh cannot be used together\n"); return NULL; } } renderer->setBBoxHierarchyType(bbhType); renderer->setScaleFactor(SkDoubleToScalar(FLAGS_scale)); return renderer.detach(); }
static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, sk_tools::PictureBenchmark*& benchmark) { const char* argv0 = argv[0]; char* const* stop = argv + argc; int repeats = DEFAULT_REPEATS; sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; // Create a string to show our current settings. // TODO: Make it prettier. Currently it just repeats the command line. SkString commandLine("bench_pictures:"); for (int i = 1; i < argc; i++) { commandLine.appendf(" %s", *(argv+i)); } commandLine.append("\n"); bool usePipe = false; bool multiThreaded = false; bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; bool isPowerOf2Mode = false; const char* mode = NULL; for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--repeat")) { ++argv; if (argv < stop) { repeats = atoi(*argv); if (repeats < 1) { SkDELETE(benchmark); gLogger.logError("--repeat must be given a value > 0\n"); exit(-1); } } else { SkDELETE(benchmark); gLogger.logError("Missing arg for --repeat\n"); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--pipe")) { usePipe = true; } else if (0 == strcmp(*argv, "--logFile")) { argv++; if (argv < stop) { if (!gLogger.SetLogFile(*argv)) { SkString str; str.printf("Could not open %s for writing.", *argv); gLogger.logError(str); usage(argv0); exit(-1); } } else { gLogger.logError("Missing arg for --logFile\n"); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--mode")) { SkDELETE(benchmark); ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --mode\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "record")) { benchmark = SkNEW(sk_tools::RecordPictureBenchmark); } else if (0 == strcmp(*argv, "simple")) { benchmark = SkNEW(sk_tools::SimplePictureBenchmark); } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { useTiles = true; mode = *argv; if (0 == strcmp(*argv, "pow2tile")) { isPowerOf2Mode = true; } ++argv; if (argv >= stop) { SkString err; err.printf("Missing width for --mode %s\n", mode); gLogger.logError(err); usage(argv0); exit(-1); } widthString = *argv; ++argv; if (argv >= stop) { gLogger.logError("Missing height for --mode tile\n"); usage(argv0); exit(-1); } heightString = *argv; ++argv; if (argv < stop && 0 == strcmp(*argv, "multi")) { multiThreaded = true; } else { --argv; } } else if (0 == strcmp(*argv, "playbackCreation")) { benchmark = SkNEW(sk_tools::PlaybackCreationBenchmark); } else { SkString err; err.printf("%s is not a valid mode for --mode\n", *argv); gLogger.logError(err); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --deivce\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "bitmap")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(*argv, "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; } #endif else { SkString err; err.printf("%s is not a valid mode for --device\n", *argv); gLogger.logError(err); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) { SkDELETE(benchmark); usage(argv0); exit(0); } else { inputs->push_back(SkString(*argv)); } } if (useTiles) { sk_tools::TiledPictureBenchmark* tileBenchmark = SkNEW(sk_tools::TiledPictureBenchmark); if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { SkDELETE(tileBenchmark); SkString err; err.printf("--mode %s must be given a width" " value that is a power of two\n", mode); gLogger.logError(err); exit(-1); } tileBenchmark->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { tileBenchmark->setTileWidthPercentage(atof(widthString)); if (!(tileBenchmark->getTileWidthPercentage() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a width percentage > 0\n"); exit(-1); } } else { tileBenchmark->setTileWidth(atoi(widthString)); if (!(tileBenchmark->getTileWidth() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a width > 0\n"); exit(-1); } } if (sk_tools::is_percentage(heightString)) { tileBenchmark->setTileHeightPercentage(atof(heightString)); if (!(tileBenchmark->getTileHeightPercentage() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a height percentage > 0\n"); exit(-1); } } else { tileBenchmark->setTileHeight(atoi(heightString)); if (!(tileBenchmark->getTileHeight() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a height > 0\n"); exit(-1); } } tileBenchmark->setThreading(multiThreaded); tileBenchmark->setUsePipe(usePipe); benchmark = tileBenchmark; } else if (usePipe) { SkDELETE(benchmark); benchmark = SkNEW(sk_tools::PipePictureBenchmark); } if (inputs->count() < 1) { SkDELETE(benchmark); usage(argv0); exit(-1); } if (NULL == benchmark) { benchmark = SkNEW(sk_tools::SimplePictureBenchmark); } benchmark->setRepeats(repeats); benchmark->setDeviceType(deviceType); benchmark->setLogger(&gLogger); // Report current settings: gLogger.logProgress(commandLine); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); GrGLGPBuilder* pb = args.fPB; GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); // emit attributes vsBuilder->emitAttributes(dfTexEffect); // setup pass through color if (!dfTexEffect.colorIgnored()) { this->setupUniformColor(pb, args.fOutputColor, &fColorUniform); } // Setup position this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // set up varyings bool isUniformScale = SkToBool(dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask); GrGLVertToFrag recipScale(kFloat_GrSLType); GrGLVertToFrag st(kVec2f_GrSLType); args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from int to float SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); SkScalar recipWidth = 1.0f / atlas->width(); SkScalar recipHeight = 1.0f / atlas->height(); GrGLVertToFrag uv(kVec2f_GrSLType); args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = vec2(%.*f, %.*f) * %s;", uv.vsOut(), GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipWidth, GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipHeight, dfTexEffect.inTextureCoords()->fName); // add frag shader code GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); // create LCD offset adjusted by inverse of transform // Use highp to work around aliasing issues fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); SkScalar lcdDelta = 1.0f / (3.0f * atlas->width()); if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { fsBuilder->codeAppendf("float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } else { fsBuilder->codeAppendf("float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } if (isUniformScale) { fsBuilder->codeAppendf("float dy = abs(dFdy(%s.y));", st.fsIn()); fsBuilder->codeAppend("vec2 offset = vec2(dy*delta, 0.0);"); } else { fsBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn()); fsBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fsBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fsBuilder->codeAppend("vec2 offset = delta*Jdx;"); } // green is distance to uv center fsBuilder->codeAppend("\tvec4 texColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppend("\tvec3 distance;\n"); fsBuilder->codeAppend("\tdistance.y = texColor.r;\n"); // red is distance to left offset fsBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n"); fsBuilder->codeAppend("\ttexColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppend("\tdistance.x = texColor.r;\n"); // blue is distance to right offset fsBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); fsBuilder->codeAppend("\ttexColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppend("\tdistance.z = texColor.r;\n"); fsBuilder->codeAppend("\tdistance = " "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));"); // adjust width based on gamma const char* distanceAdjustUniName = NULL; fDistanceAdjustUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec3f_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); fsBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); // To be strictly correct, we should compute the anti-aliasing factor separately // for each color component. However, this is only important when using perspective // transformations, and even then using a single factor seems like a reasonable // trade-off between quality and speed. fsBuilder->codeAppend("float afwidth;"); if (isUniformScale) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*dy;"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fsBuilder->codeAppend("} else {"); fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fsBuilder->codeAppend("}"); fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fsBuilder->codeAppend( "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);"); fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(cte); // compute numbers to be hardcoded to convert texture coordinates from int to float SkASSERT(cte.numTextures() == 1); SkDEBUGCODE(GrTexture* atlas = cte.textureAccess(0).getTexture()); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); GrGLSLVertToFrag v(kVec2f_GrSLType); varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cte.inTextureCoords()->fName); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!cte.colorIgnored()) { if (cte.hasVertexColor()) { varyingHandler->addPassThroughAttribute(cte.inColor(), args.fOutputColor); } else { this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } } // Setup position this->setupPosition(vertBuilder, gpArgs, cte.inPosition()->fName); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, cte.inPosition()->fName, cte.localMatrix(), args.fTransformsIn, args.fTransformsOut); if (cte.maskFormat() == kARGB_GrMaskFormat) { fragBuilder->codeAppendf("%s = ", args.fOutputColor); fragBuilder->appendTextureLookupAndModulate(args.fOutputColor, args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType); fragBuilder->codeAppend(";"); fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage); } else { fragBuilder->codeAppendf("%s = ", args.fOutputCoverage); fragBuilder->appendTextureLookup(args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType); fragBuilder->codeAppend(";"); if (cte.maskFormat() == kA565_GrMaskFormat) { // set alpha to be max of rgb coverage fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);", args.fOutputCoverage, args.fOutputCoverage, args.fOutputCoverage, args.fOutputCoverage); } } }
static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, sk_tools::PictureRenderer*& renderer, SkString*& outputDir, bool* validate, bool* writeWholeImage, int* clones){ const char* argv0 = argv[0]; char* const* stop = argv + argc; sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; bool usePipe = false; int numThreads = 1; bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; int gridWidth = 0; int gridHeight = 0; bool isPowerOf2Mode = false; bool isCopyMode = false; const char* xTilesString = NULL; const char* yTilesString = NULL; const char* mode = NULL; bool gridSupported = false; sk_tools::PictureRenderer::BBoxHierarchyType bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; *validate = false; *writeWholeImage = false; *clones = 0; SkISize viewport; viewport.setEmpty(); SkScalar scaleFactor = SK_Scalar1; for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--mode")) { if (renderer != NULL) { renderer->unref(); SkDebugf("Cannot combine modes.\n"); usage(argv0); exit(-1); } ++argv; if (argv >= stop) { SkDebugf("Missing mode for --mode\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "simple")) { renderer = SkNEW(sk_tools::SimplePictureRenderer); } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile")) || 0 == strcmp(*argv, "copyTile")) { useTiles = true; mode = *argv; if (0 == strcmp(*argv, "pow2tile")) { isPowerOf2Mode = true; } else if (0 == strcmp(*argv, "copyTile")) { isCopyMode = true; } else { gridSupported = true; } ++argv; if (argv >= stop) { SkDebugf("Missing width for --mode %s\n", mode); usage(argv0); exit(-1); } widthString = *argv; ++argv; if (argv >= stop) { SkDebugf("Missing height for --mode %s\n", mode); usage(argv0); exit(-1); } heightString = *argv; } else if (0 == strcmp(*argv, "rerecord")) { renderer = SkNEW(sk_tools::RecordPictureRenderer); } else { SkDebugf("%s is not a valid mode for --mode\n", *argv); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--bbh")) { ++argv; if (argv >= stop) { SkDebugf("Missing value for --bbh\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "none")) { bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; } else if (0 == strcmp(*argv, "rtree")) { bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType; } else if (0 == strcmp(*argv, "grid")) { bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType; ++argv; if (argv >= stop) { SkDebugf("Missing width for --bbh grid\n"); usage(argv0); exit(-1); } gridWidth = atoi(*argv); ++argv; if (argv >= stop) { SkDebugf("Missing height for --bbh grid\n"); usage(argv0); exit(-1); } gridHeight = atoi(*argv); } else { SkDebugf("%s is not a valid value for --bbhType\n", *argv); usage(argv0); exit(-1);; } } else if (0 == strcmp(*argv, "--viewport")) { ++argv; if (argv >= stop) { SkDebugf("Missing width for --viewport\n"); usage(argv0); exit(-1); } viewport.fWidth = atoi(*argv); ++argv; if (argv >= stop) { SkDebugf("Missing height for --viewport\n"); usage(argv0); exit(-1); } viewport.fHeight = atoi(*argv); } else if (0 == strcmp(*argv, "--scale")) { ++argv; if (argv >= stop) { SkDebugf("Missing scaleFactor for --scale\n"); usage(argv0); exit(-1); } scaleFactor = SkDoubleToScalar(atof(*argv)); } else if (0 == strcmp(*argv, "--tiles")) { ++argv; if (argv >= stop) { SkDebugf("Missing x for --tiles\n"); usage(argv0); exit(-1); } xTilesString = *argv; ++argv; if (argv >= stop) { SkDebugf("Missing y for --tiles\n"); usage(argv0); exit(-1); } yTilesString = *argv; } else if (0 == strcmp(*argv, "--pipe")) { usePipe = true; } else if (0 == strcmp(*argv, "--multi")) { ++argv; if (argv >= stop) { SkSafeUnref(renderer); SkDebugf("Missing arg for --multi\n"); usage(argv0); exit(-1); } numThreads = atoi(*argv); if (numThreads < 2) { SkSafeUnref(renderer); SkDebugf("Number of threads must be at least 2.\n"); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--clone")) { ++argv; if (argv >= stop) { SkSafeUnref(renderer); SkDebugf("Missing arg for --clone\n"); usage(argv0); exit(-1); } *clones = atoi(*argv); if (*clones < 0) { SkSafeUnref(renderer); SkDebugf("Number of clones must be at least 0.\n"); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { SkSafeUnref(renderer); SkDebugf("Missing mode for --device\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "bitmap")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(*argv, "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; } #endif else { SkSafeUnref(renderer); SkDebugf("%s is not a valid mode for --device\n", *argv); usage(argv0); exit(-1); } } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) { SkSafeUnref(renderer); usage(argv0); exit(-1); } else if (0 == strcmp(*argv, "-w")) { ++argv; if (argv >= stop) { SkDebugf("Missing output directory for -w\n"); usage(argv0); exit(-1); } outputDir = SkNEW_ARGS(SkString, (*argv)); } else if (0 == strcmp(*argv, "--validate")) { *validate = true; } else if (0 == strcmp(*argv, "--writeWholeImage")) { *writeWholeImage = true; } else { inputs->push_back(SkString(*argv)); } } if (numThreads > 1 && !useTiles) { SkSafeUnref(renderer); SkDebugf("Multithreaded drawing requires tiled rendering.\n"); usage(argv0); exit(-1); } if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { SkDebugf("--pipe and --bbh cannot be used together\n"); usage(argv0); exit(-1); } if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType && !gridSupported) { SkDebugf("'--bbh grid' is not compatible with specified --mode.\n"); usage(argv0); exit(-1); } if (useTiles) { SkASSERT(NULL == renderer); sk_tools::TiledPictureRenderer* tiledRenderer; if (isCopyMode) { int x, y; if (xTilesString != NULL) { SkASSERT(yTilesString != NULL); x = atoi(xTilesString); y = atoi(yTilesString); if (x <= 0 || y <= 0) { SkDebugf("--tiles must be given values > 0\n"); usage(argv0); exit(-1); } } else { x = y = 4; } tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)); } else if (numThreads > 1) { tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads)); } else { tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); } if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { tiledRenderer->unref(); SkString err; err.printf("-mode %s must be given a width" " value that is a power of two\n", mode); SkDebugf(err.c_str()); usage(argv0); exit(-1); } tiledRenderer->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { if (isCopyMode) { tiledRenderer->unref(); SkString err; err.printf("--mode %s does not support percentages.\n", mode); SkDebugf(err.c_str()); usage(argv0); exit(-1); } tiledRenderer->setTileWidthPercentage(atof(widthString)); if (!(tiledRenderer->getTileWidthPercentage() > 0)) { tiledRenderer->unref(); SkDebugf("--mode %s must be given a width percentage > 0\n", mode); usage(argv0); exit(-1); } } else { tiledRenderer->setTileWidth(atoi(widthString)); if (!(tiledRenderer->getTileWidth() > 0)) { tiledRenderer->unref(); SkDebugf("--mode %s must be given a width > 0\n", mode); usage(argv0); exit(-1); } } if (sk_tools::is_percentage(heightString)) { if (isCopyMode) { tiledRenderer->unref(); SkString err; err.printf("--mode %s does not support percentages.\n", mode); SkDebugf(err.c_str()); usage(argv0); exit(-1); } tiledRenderer->setTileHeightPercentage(atof(heightString)); if (!(tiledRenderer->getTileHeightPercentage() > 0)) { tiledRenderer->unref(); SkDebugf("--mode %s must be given a height percentage > 0\n", mode); usage(argv0); exit(-1); } } else { tiledRenderer->setTileHeight(atoi(heightString)); if (!(tiledRenderer->getTileHeight() > 0)) { tiledRenderer->unref(); SkDebugf("--mode %s must be given a height > 0\n", mode); usage(argv0); exit(-1); } } if (numThreads > 1) { #if SK_SUPPORT_GPU if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { tiledRenderer->unref(); SkDebugf("GPU not compatible with multithreaded tiling.\n"); usage(argv0); exit(-1); } #endif } renderer = tiledRenderer; if (usePipe) { SkDebugf("Pipe rendering is currently not compatible with tiling.\n" "Turning off pipe.\n"); } } else if (usePipe) { if (renderer != NULL) { renderer->unref(); SkDebugf("Pipe is incompatible with other modes.\n"); usage(argv0); exit(-1); } renderer = SkNEW(sk_tools::PipePictureRenderer); } if (inputs->empty()) { SkSafeUnref(renderer); if (NULL != outputDir) { SkDELETE(outputDir); } usage(argv0); exit(-1); } if (NULL == renderer) { renderer = SkNEW(sk_tools::SimplePictureRenderer); } renderer->setBBoxHierarchyType(bbhType); renderer->setGridSize(gridWidth, gridHeight); renderer->setViewport(viewport); renderer->setScaleFactor(scaleFactor); renderer->setDeviceType(deviceType); }