const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) { if (caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(config)) { if (caps.textureRedSupport()) { static const GrGLenum gRedSmear[] = { GR_GL_RED, GR_GL_RED, GR_GL_RED, GR_GL_RED }; return gRedSmear; } else { static const GrGLenum gAlphaSmear[] = { GR_GL_ALPHA, GR_GL_ALPHA, GR_GL_ALPHA, GR_GL_ALPHA }; return gAlphaSmear; } } else { static const GrGLenum gStraight[] = { GR_GL_RED, GR_GL_GREEN, GR_GL_BLUE, GR_GL_ALPHA }; return gStraight; } }
GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, SkAlphaType alphaType, const SkIRect& contentArea, uint32_t uniqueID, SkColorSpace* cs) : INHERITED(contentArea.width(), contentArea.height(), GrPixelConfigIsAlphaOnly(original->config())) , fOriginal(original) , fAlphaType(alphaType) , fColorSpace(cs) , fUniqueID(uniqueID) { SkASSERT(SkIRect::MakeWH(original->width(), original->height()).contains(contentArea)); if (contentArea.fLeft > 0 || contentArea.fTop > 0 || contentArea.fRight < original->width() || contentArea.fBottom < original->height()) { fContentArea.set(contentArea); } }
const GrFragmentProcessor* SkImageShader::asFragmentProcessor(GrContext* context, const SkMatrix& viewM, const SkMatrix* localMatrix, SkFilterQuality filterQuality) const { SkMatrix matrix; matrix.setIDiv(fImage->width(), fImage->height()); SkMatrix lmInverse; if (!this->getLocalMatrix().invert(&lmInverse)) { return nullptr; } if (localMatrix) { SkMatrix inv; if (!localMatrix->invert(&inv)) { return nullptr; } lmInverse.postConcat(inv); } matrix.preConcat(lmInverse); SkShader::TileMode tm[] = { fTileModeX, fTileModeY }; // Must set wrap and filter on the sampler before requesting a texture. In two places below // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. bool doBicubic; GrTextureParams::FilterMode textureFilterMode = GrSkFilterQualityToGrFilterMode(filterQuality, viewM, this->getLocalMatrix(), &doBicubic); GrTextureParams params(tm, textureFilterMode); SkAutoTUnref<GrTexture> texture(as_IB(fImage)->asTextureRef(context, params)); if (!texture) { return nullptr; } SkAutoTUnref<const GrFragmentProcessor> inner; if (doBicubic) { inner.reset(GrBicubicEffect::Create(texture, matrix, tm)); } else { inner.reset(GrSimpleTextureEffect::Create(texture, matrix, params)); } if (GrPixelConfigIsAlphaOnly(texture->config())) { return SkRef(inner.get()); } return GrFragmentProcessor::MulOutputByInputAlpha(inner); }
std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor( const GrFPArgs& args) const { const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix); SkMatrix lmInverse; if (!lm->invert(&lmInverse)) { return nullptr; } GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX), tile_mode_to_wrap_mode(fTileModeY)}; // Must set wrap and filter on the sampler before requesting a texture. In two places below // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. bool doBicubic; GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode( args.fFilterQuality, *args.fViewMatrix, *lm, args.fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic); GrSamplerState samplerState(wrapModes, textureFilterMode); sk_sp<SkColorSpace> texColorSpace; SkScalar scaleAdjust[2] = { 1.0f, 1.0f }; sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef( args.fContext, samplerState, args.fDstColorSpaceInfo->colorSpace(), &texColorSpace, scaleAdjust)); if (!proxy) { return nullptr; } GrPixelConfig config = proxy->config(); bool isAlphaOnly = GrPixelConfigIsAlphaOnly(config); lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]); std::unique_ptr<GrFragmentProcessor> inner; if (doBicubic) { inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes); } else { inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState); } inner = GrColorSpaceXformEffect::Make(std::move(inner), texColorSpace.get(), config, args.fDstColorSpaceInfo->colorSpace()); if (isAlphaOnly) { return inner; } return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner)); }
GrGLShaderBuilder::DstReadKey GrGLShaderBuilder::KeyForDstRead(const GrTexture* dstCopy, const GrGLCaps& caps) { uint32_t key = kYesDstRead_DstReadKeyBit; if (caps.fbFetchSupport()) { return key; } SkASSERT(NULL != dstCopy); if (!caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(dstCopy->config())) { // The fact that the config is alpha-only must be considered when generating code. key |= kUseAlphaConfig_DstReadKeyBit; } if (kTopLeft_GrSurfaceOrigin == dstCopy->origin()) { key |= kTopLeftOrigin_DstReadKeyBit; } SkASSERT(static_cast<DstReadKey>(key) == key); return static_cast<DstReadKey>(key); }
void GrTextContext::flushGlyphs() { if (NULL == fDrawTarget) { return; } GrDrawState* drawState = fDrawTarget->drawState(); if (fCurrVertex > 0) { // setup our sampler state for our text texture/atlas drawState->stage(kGlyphMaskStage)->reset(); GrAssert(GrIsALIGN4(fCurrVertex)); GrAssert(fCurrTexture); GrTextureParams params(SkShader::kRepeat_TileMode, false); drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, SkMatrix::I(), params); if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || fPaint.hasColorStage()) { GrPrintf("LCD Text will not draw correctly.\n"); } // setup blend so that we get mask * paintColor + (1-mask)*dstColor drawState->setBlendConstant(fPaint.getColor()); drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); // don't modulate by the paint's color in the frag since we're // already doing it via the blend const. drawState->setColor(0xffffffff); } else { // set back to normal in case we took LCD path previously. drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); drawState->setColor(fPaint.getColor()); } int nGlyphs = fCurrVertex / 4; fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, nGlyphs, 4, 6); fDrawTarget->resetVertexSource(); fVertices = NULL; fMaxVertices = 0; fCurrVertex = 0; GrSafeSetNull(fCurrTexture); } drawState->disableStages(); fDrawTarget = NULL; }
void GrVkCaps::initGLSLCaps(const VkPhysicalDeviceProperties& properties, uint32_t featureFlags) { GrGLSLCaps* glslCaps = static_cast<GrGLSLCaps*>(fShaderCaps.get()); glslCaps->fVersionDeclString = "#version 330\n"; // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config. for (int i = 0; i < kGrPixelConfigCnt; ++i) { GrPixelConfig config = static_cast<GrPixelConfig>(i); if (GrPixelConfigIsAlphaOnly(config)) { glslCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR(); glslCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA(); } else { glslCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA(); } } // Vulkan is based off ES 3.0 so the following should all be supported glslCaps->fUsesPrecisionModifiers = true; glslCaps->fFlatInterpolationSupport = true; // GrShaderCaps glslCaps->fShaderDerivativeSupport = true; glslCaps->fGeometryShaderSupport = SkToBool(featureFlags & kGeometryShader_GrVkFeatureFlag); glslCaps->fDualSourceBlendingSupport = SkToBool(featureFlags & kDualSrcBlend_GrVkFeatureFlag); glslCaps->fIntegerSupport = true; glslCaps->fMaxVertexSamplers = glslCaps->fMaxGeometrySamplers = glslCaps->fMaxFragmentSamplers = SkTMin(properties.limits.maxPerStageDescriptorSampledImages, properties.limits.maxPerStageDescriptorSamplers); glslCaps->fMaxCombinedSamplers = SkTMin(properties.limits.maxDescriptorSetSampledImages, properties.limits.maxDescriptorSetSamplers); }
void GrVkCaps::initGLSLCaps(const VkPhysicalDeviceProperties& properties, uint32_t featureFlags) { GrGLSLCaps* glslCaps = static_cast<GrGLSLCaps*>(fShaderCaps.get()); glslCaps->fVersionDeclString = "#version 330\n"; // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config. for (int i = 0; i < kGrPixelConfigCnt; ++i) { GrPixelConfig config = static_cast<GrPixelConfig>(i); if (GrPixelConfigIsAlphaOnly(config)) { glslCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR(); glslCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA(); } else { if (kRGBA_4444_GrPixelConfig == config) { // The vulkan spec does not require R4G4B4A4 to be supported for texturing so we // store the data in a B4G4R4A4 texture and then swizzle it when doing texture reads // or writing to outputs. Since we're not actually changing the data at all, the // only extra work is the swizzle in the shader for all operations. glslCaps->fConfigTextureSwizzle[i] = GrSwizzle::BGRA(); glslCaps->fConfigOutputSwizzle[i] = GrSwizzle::BGRA(); } else { glslCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA(); } } } // Vulkan is based off ES 3.0 so the following should all be supported glslCaps->fUsesPrecisionModifiers = true; glslCaps->fFlatInterpolationSupport = true; // GrShaderCaps glslCaps->fShaderDerivativeSupport = true; glslCaps->fGeometryShaderSupport = SkToBool(featureFlags & kGeometryShader_GrVkFeatureFlag); glslCaps->fDualSourceBlendingSupport = SkToBool(featureFlags & kDualSrcBlend_GrVkFeatureFlag); glslCaps->fIntegerSupport = true; // Assume the minimum precisions mandated by the SPIR-V spec. glslCaps->fShaderPrecisionVaries = true; for (int s = 0; s < kGrShaderTypeCount; ++s) { auto& highp = glslCaps->fFloatPrecisions[s][kHigh_GrSLPrecision]; highp.fLogRangeLow = highp.fLogRangeHigh = 127; highp.fBits = 23; auto& mediump = glslCaps->fFloatPrecisions[s][kMedium_GrSLPrecision]; mediump.fLogRangeLow = mediump.fLogRangeHigh = 14; mediump.fBits = 10; glslCaps->fFloatPrecisions[s][kLow_GrSLPrecision] = mediump; } glslCaps->initSamplerPrecisionTable(); glslCaps->fMaxVertexSamplers = glslCaps->fMaxGeometrySamplers = glslCaps->fMaxFragmentSamplers = SkTMin(SkTMin(properties.limits.maxPerStageDescriptorSampledImages, properties.limits.maxPerStageDescriptorSamplers), (uint32_t)INT_MAX); glslCaps->fMaxCombinedSamplers = SkTMin(SkTMin(properties.limits.maxDescriptorSetSampledImages, properties.limits.maxDescriptorSetSamplers), (uint32_t)INT_MAX); }
bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) { bool usingTextures[kNumStages]; for (int s = 0; s < kNumStages; ++s) { usingTextures[s] = VertexUsesStage(s, fGeometrySrc.fVertexLayout); if (usingTextures[s] && fCurrDrawState.fSamplerStates[s].isGradient()) { unimpl("Fixed pipe doesn't support radial/sweep gradients"); return false; } } if (GR_GL_SUPPORT_ES1) { if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) || BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) { unimpl("ES1 doesn't support blend constant"); return false; } } if (!flushGLStateCommon(type)) { return false; } if (fDirtyFlags.fRenderTargetChanged) { flushProjectionMatrix(); } for (int s = 0; s < kNumStages; ++s) { bool wasUsingTexture = VertexUsesStage(s, fHWGeometryState.fVertexLayout); if (usingTextures[s] != wasUsingTexture) { setTextureUnit(s); if (usingTextures[s]) { GR_GL(Enable(GR_GL_TEXTURE_2D)); } else { GR_GL(Disable(GR_GL_TEXTURE_2D)); } } } uint32_t vertColor = (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit); uint32_t prevVertColor = (fHWGeometryState.fVertexLayout & kColor_VertexLayoutBit); if (vertColor != prevVertColor) { if (vertColor) { GR_GL(ShadeModel(GR_GL_SMOOTH)); // invalidate the immediate mode color fHWDrawState.fColor = GrColor_ILLEGAL; } else { GR_GL(ShadeModel(GR_GL_FLAT)); } } if (!vertColor && fHWDrawState.fColor != fCurrDrawState.fColor) { GR_GL(Color4ub(GrColorUnpackR(fCurrDrawState.fColor), GrColorUnpackG(fCurrDrawState.fColor), GrColorUnpackB(fCurrDrawState.fColor), GrColorUnpackA(fCurrDrawState.fColor))); fHWDrawState.fColor = fCurrDrawState.fColor; } // set texture environment, decide whether we are modulating by RGB or A. for (int s = 0; s < kNumStages; ++s) { if (usingTextures[s]) { GrGLTexture* texture = (GrGLTexture*)fCurrDrawState.fTextures[s]; if (NULL != texture) { TextureEnvRGBOperands nextRGBOperand0 = (GrPixelConfigIsAlphaOnly(texture->config())) ? kAlpha_TextureEnvRGBOperand : kColor_TextureEnvRGBOperand; if (fHWRGBOperand0[s] != nextRGBOperand0) { setTextureUnit(s); GR_GL(TexEnvi(GR_GL_TEXTURE_ENV, GR_GL_OPERAND0_RGB, (nextRGBOperand0==kAlpha_TextureEnvRGBOperand) ? GR_GL_SRC_ALPHA : GR_GL_SRC_COLOR)); fHWRGBOperand0[s] = nextRGBOperand0; } if (((1 << s) & fDirtyFlags.fTextureChangedMask) || (fHWDrawState.fSamplerStates[s].getMatrix() != getSamplerMatrix(s))) { GrMatrix texMat = getSamplerMatrix(s); AdjustTextureMatrix(texture, GrSamplerState::kNormal_SampleMode, &texMat); GrGpuMatrix glm; glm.set(texMat); setTextureUnit(s); GR_GL(MatrixMode(GR_GL_TEXTURE)); GR_GL(LoadMatrixf(glm.fMat)); recordHWSamplerMatrix(s, getSamplerMatrix(s)); } } else { GrAssert(!"Rendering with texture vert flag set but no bound texture"); return false; } } } if (fHWDrawState.fViewMatrix != fCurrDrawState.fViewMatrix) { GrGpuMatrix glm; glm.set(fCurrDrawState.fViewMatrix); GR_GL(MatrixMode(GR_GL_MODELVIEW)); GR_GL(LoadMatrixf(glm.fMat)); fHWDrawState.fViewMatrix = fCurrDrawState.fViewMatrix; } resetDirtyFlags(); return true; }
static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, const CopyParams& copyParams) { SkASSERT(!subset || !subset->isEmpty()); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = copyParams.fWidth; rtDesc.fHeight = copyParams.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else { return nullptr; } } SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, SkBudgeted::kYes)); if (!copy) { return nullptr; } // TODO: If no scaling is being performed then use copySurface. GrPaint paint; paint.setGammaCorrect(true); // TODO: Initializing these values for no reason cause the compiler is complaining SkScalar sx = 0.f; SkScalar sy = 0.f; if (subset) { sx = 1.f / inputTexture->width(); sy = 1.f / inputTexture->height(); } if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset && (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { SkRect domain; domain.fLeft = (subset->fLeft + 0.5f) * sx; domain.fTop = (subset->fTop + 0.5f)* sy; domain.fRight = (subset->fRight - 0.5f) * sx; domain.fBottom = (subset->fBottom - 0.5f) * sy; // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode); paint.addColorFragmentProcessor( GrTextureDomainEffect::Make(inputTexture, SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter)); } else { GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkRect localRect; if (subset) { localRect = SkRect::Make(*subset); localRect.fLeft *= sx; localRect.fTop *= sy; localRect.fRight *= sx; localRect.fBottom *= sy; } else { localRect = SkRect::MakeWH(1.f, 1.f); } sk_sp<GrDrawContext> drawContext(context->drawContext(sk_ref_sp(copy->asRenderTarget()))); if (!drawContext) { return nullptr; } SkRect dstRect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); drawContext->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect); return copy.release(); }
void GrGpuGLShaders::buildProgram(GrPrimitiveType type) { // Must initialize all fields or cache will have false negatives! fCurrentProgram.fProgramDesc.fVertexLayout = fGeometrySrc.fVertexLayout; fCurrentProgram.fProgramDesc.fOptFlags = 0; if (kPoints_PrimitiveType != type) { fCurrentProgram.fProgramDesc.fOptFlags |= GrGLProgram::ProgramDesc::kNotPoints_OptFlagBit; } #if GR_AGGRESSIVE_SHADER_OPTS if (!(fCurrentProgram.fProgramDesc.fVertexLayout & kColor_VertexLayoutBit) && (0xffffffff == fCurrDrawState.fColor)) { fCurrentProgram.fProgramDesc.fOptFlags |= GrGLProgram::ProgramDesc::kVertexColorAllOnes_OptFlagBit; } #endif for (int s = 0; s < kNumStages; ++s) { GrGLProgram::ProgramDesc::StageDesc& stage = fCurrentProgram.fProgramDesc.fStages[s]; stage.fEnabled = VertexUsesStage(s, fGeometrySrc.fVertexLayout); if (stage.fEnabled) { GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s]; GrAssert(NULL != texture); // we matrix to invert when orientation is TopDown, so make sure // we aren't in that case before flagging as identity. if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) { stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit; } else if (!getSamplerMatrix(s).hasPerspective()) { stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit; } else { stage.fOptFlags = 0; } switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) { case GrSamplerState::kNormal_SampleMode: stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kIdentity_CoordMapping; break; case GrSamplerState::kRadial_SampleMode: stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadialGradient_CoordMapping; break; case GrSamplerState::kRadial2_SampleMode: stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping; break; case GrSamplerState::kSweep_SampleMode: stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kSweepGradient_CoordMapping; break; default: GrAssert(!"Unexpected sample mode!"); break; } if (GrPixelConfigIsAlphaOnly(texture->config())) { stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation; } else { stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation; } if (fCurrDrawState.fEffects[s]) { fCurrentProgram.fStageEffects[s] = GrGLEffect::Create(fCurrDrawState.fEffects[s]); } else { delete fCurrentProgram.fStageEffects[s]; fCurrentProgram.fStageEffects[s] = NULL; } } else { stage.fOptFlags = 0; stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0; stage.fModulation = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0; fCurrentProgram.fStageEffects[s] = NULL; } } }
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be // set on the new texture. stretch controls whether the scaling is done using nearest or bilerp // filtering and the size to stretch the texture to. GrTexture* stretch_texture(GrTexture* inputTexture, const SkGrStretch& stretch, SkPixelRef* pixelRef, const GrUniqueKey& optionalKey) { SkASSERT(SkGrStretch::kNone_Type != stretch.fType); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = stretch.fWidth; rtDesc.fHeight = stretch.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else { return nullptr; } } SkAutoTUnref<GrTexture> stretched(GrCreateTextureForPixels(context, optionalKey, rtDesc, pixelRef, nullptr,0)); if (!stretched) { return nullptr; } GrPaint paint; // If filtering is not desired then we want to ensure all texels in the resampled image are // copies of texels from the original. GrTextureParams params(SkShader::kClamp_TileMode, SkGrStretch::kBilerp_Type == stretch.fType ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); SkRect localRect = SkRect::MakeWH(1.f, 1.f); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext()); if (!drawContext) { return nullptr; } drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); return stretched.detach(); }
void GrGpuGLShaders::buildProgram(GrPrimitiveType type, BlendOptFlags blendOpts, GrBlendCoeff dstCoeff, GrCustomStage** customStages) { ProgramDesc& desc = fCurrentProgram.fProgramDesc; const GrDrawState& drawState = this->getDrawState(); // This should already have been caught GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts)); bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag); bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag | kEmitCoverage_BlendOptFlag)); // The descriptor is used as a cache key. Thus when a field of the // descriptor will not affect program generation (because of the vertex // layout in use or other descriptor field settings) it should be set // to a canonical value to avoid duplicate programs with different keys. // Must initialize all fields or cache will have false negatives! desc.fVertexLayout = this->getVertexLayout(); desc.fEmitsPointSize = kPoints_PrimitiveType == type; bool requiresAttributeColors = !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit); bool requiresAttributeCoverage = !skipCoverage && SkToBool(desc.fVertexLayout & kCoverage_VertexLayoutBit); // fColorInput/fCoverageInput records how colors are specified for the. // program. So we strip the bits from the layout to avoid false negatives // when searching for an existing program in the cache. desc.fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit); desc.fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode(); desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit); // no reason to do edge aa or look at per-vertex coverage if coverage is // ignored if (skipCoverage) { desc.fVertexLayout &= ~(kEdge_VertexLayoutBit | kCoverage_VertexLayoutBit); } bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag); bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) || (!requiresAttributeColors && 0xffffffff == drawState.getColor()); if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) { desc.fColorInput = ProgramDesc::kTransBlack_ColorInput; } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) { desc.fColorInput = ProgramDesc::kSolidWhite_ColorInput; } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) { desc.fColorInput = ProgramDesc::kUniform_ColorInput; } else { desc.fColorInput = ProgramDesc::kAttribute_ColorInput; } bool covIsSolidWhite = !requiresAttributeCoverage && 0xffffffff == drawState.getCoverage(); if (skipCoverage) { desc.fCoverageInput = ProgramDesc::kTransBlack_ColorInput; } else if (covIsSolidWhite) { desc.fCoverageInput = ProgramDesc::kSolidWhite_ColorInput; } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) { desc.fCoverageInput = ProgramDesc::kUniform_ColorInput; } else { desc.fCoverageInput = ProgramDesc::kAttribute_ColorInput; } int lastEnabledStage = -1; if (!skipCoverage && (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit)) { desc.fVertexEdgeType = drawState.getVertexEdgeType(); } else { // use canonical value when not set to avoid cache misses desc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType; } for (int s = 0; s < GrDrawState::kNumStages; ++s) { StageDesc& stage = desc.fStages[s]; stage.fOptFlags = 0; stage.setEnabled(this->isStageEnabled(s)); bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage; if (!skip && stage.isEnabled()) { lastEnabledStage = s; const GrGLTexture* texture = static_cast<const GrGLTexture*>(drawState.getTexture(s)); GrAssert(NULL != texture); const GrSamplerState& sampler = drawState.getSampler(s); // we matrix to invert when orientation is TopDown, so make sure // we aren't in that case before flagging as identity. if (TextureMatrixIsIdentity(texture, sampler)) { stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit; } else if (!sampler.getMatrix().hasPerspective()) { stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit; } switch (sampler.getSampleMode()) { case GrSamplerState::kNormal_SampleMode: stage.fCoordMapping = StageDesc::kIdentity_CoordMapping; break; case GrSamplerState::kRadial_SampleMode: stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping; break; case GrSamplerState::kRadial2_SampleMode: if (sampler.radial2IsDegenerate()) { stage.fCoordMapping = StageDesc::kRadial2GradientDegenerate_CoordMapping; } else { stage.fCoordMapping = StageDesc::kRadial2Gradient_CoordMapping; } break; case GrSamplerState::kSweep_SampleMode: stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping; break; default: GrCrash("Unexpected sample mode!"); break; } switch (sampler.getFilter()) { // these both can use a regular texture2D() case GrSamplerState::kNearest_Filter: case GrSamplerState::kBilinear_Filter: stage.fFetchMode = StageDesc::kSingle_FetchMode; break; // performs 4 texture2D()s case GrSamplerState::k4x4Downsample_Filter: stage.fFetchMode = StageDesc::k2x2_FetchMode; break; // performs fKernelWidth texture2D()s case GrSamplerState::kConvolution_Filter: stage.fFetchMode = StageDesc::kConvolution_FetchMode; break; case GrSamplerState::kDilate_Filter: stage.fFetchMode = StageDesc::kDilate_FetchMode; break; case GrSamplerState::kErode_Filter: stage.fFetchMode = StageDesc::kErode_FetchMode; break; default: GrCrash("Unexpected filter!"); break; } if (sampler.hasTextureDomain()) { GrAssert(GrSamplerState::kClamp_WrapMode == sampler.getWrapX() && GrSamplerState::kClamp_WrapMode == sampler.getWrapY()); stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit; } stage.fInConfigFlags = 0; if (!this->glCaps().textureSwizzleSupport()) { if (GrPixelConfigIsAlphaOnly(texture->config())) { // if we don't have texture swizzle support then // the shader must smear the single channel after // reading the texture if (this->glCaps().textureRedSupport()) { // we can use R8 textures so use kSmearRed stage.fInConfigFlags |= StageDesc::kSmearRed_InConfigFlag; } else { // we can use A8 textures so use kSmearAlpha stage.fInConfigFlags |= StageDesc::kSmearAlpha_InConfigFlag; } } else if (sampler.swapsRAndB()) { stage.fInConfigFlags |= StageDesc::kSwapRAndB_InConfigFlag; } } if (GrPixelConfigIsUnpremultiplied(texture->config())) { // The shader generator assumes that color channels are bytes // when rounding. GrAssert(4 == GrBytesPerPixel(texture->config())); if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) { stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag; } else { stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag; } } if (sampler.getFilter() == GrSamplerState::kDilate_Filter || sampler.getFilter() == GrSamplerState::kErode_Filter) { stage.fKernelWidth = sampler.getKernelWidth(); } else { stage.fKernelWidth = 0; } setup_custom_stage(&stage, sampler, customStages, &fCurrentProgram, s); } else { stage.fOptFlags = 0; stage.fCoordMapping = (StageDesc::CoordMapping) 0; stage.fInConfigFlags = 0; stage.fFetchMode = (StageDesc::FetchMode) 0; stage.fKernelWidth = 0; stage.fCustomStageKey = 0; customStages[s] = NULL; } } if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) { // The shader generator assumes that color channels are bytes // when rounding. GrAssert(4 == GrBytesPerPixel(drawState.getRenderTarget()->config())); if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) { desc.fOutputConfig = ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig; } else { desc.fOutputConfig = ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig; } } else { desc.fOutputConfig = ProgramDesc::kPremultiplied_OutputConfig; } desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput; // currently the experimental GS will only work with triangle prims // (and it doesn't do anything other than pass through values from // the VS to the FS anyway). #if 0 && GR_GL_EXPERIMENTAL_GS desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport; #endif // we want to avoid generating programs with different "first cov stage" // values when they would compute the same result. // We set field in the desc to kNumStages when either there are no // coverage stages or the distinction between coverage and color is // immaterial. int firstCoverageStage = GrDrawState::kNumStages; desc.fFirstCoverageStage = GrDrawState::kNumStages; bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage; if (hasCoverage) { firstCoverageStage = drawState.getFirstCoverageStage(); } // other coverage inputs if (!hasCoverage) { hasCoverage = requiresAttributeCoverage || (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit); } if (hasCoverage) { // color filter is applied between color/coverage computation if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) { desc.fFirstCoverageStage = firstCoverageStage; } if (this->getCaps().fDualSourceBlendingSupport && !(blendOpts & (kEmitCoverage_BlendOptFlag | kCoverageAsAlpha_BlendOptFlag))) { if (kZero_BlendCoeff == dstCoeff) { // write the coverage value to second color desc.fDualSrcOutput = ProgramDesc::kCoverage_DualSrcOutput; desc.fFirstCoverageStage = firstCoverageStage; } else if (kSA_BlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially // cover desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput; desc.fFirstCoverageStage = firstCoverageStage; } else if (kSC_BlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially // cover desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput; desc.fFirstCoverageStage = firstCoverageStage; } } } }
void GrVkCaps::initShaderCaps(const VkPhysicalDeviceProperties& properties, uint32_t featureFlags) { GrShaderCaps* shaderCaps = fShaderCaps.get(); shaderCaps->fVersionDeclString = "#version 330\n"; // fConfigOutputSwizzle will default to RGBA so we only need to set it for alpha only config. for (int i = 0; i < kGrPixelConfigCnt; ++i) { GrPixelConfig config = static_cast<GrPixelConfig>(i); if (GrPixelConfigIsAlphaOnly(config)) { shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRR(); shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA(); } else { if (kGray_8_GrPixelConfig == config) { shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RRRA(); } else if (kRGBA_4444_GrPixelConfig == config) { // The vulkan spec does not require R4G4B4A4 to be supported for texturing so we // store the data in a B4G4R4A4 texture and then swizzle it when doing texture reads // or writing to outputs. Since we're not actually changing the data at all, the // only extra work is the swizzle in the shader for all operations. shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::BGRA(); shaderCaps->fConfigOutputSwizzle[i] = GrSwizzle::BGRA(); } else { shaderCaps->fConfigTextureSwizzle[i] = GrSwizzle::RGBA(); } } } if (kImagination_VkVendor == properties.vendorID) { shaderCaps->fAtan2ImplementedAsAtanYOverX = true; } // Vulkan is based off ES 3.0 so the following should all be supported shaderCaps->fUsesPrecisionModifiers = true; shaderCaps->fFlatInterpolationSupport = true; // GrShaderCaps shaderCaps->fShaderDerivativeSupport = true; shaderCaps->fGeometryShaderSupport = SkToBool(featureFlags & kGeometryShader_GrVkFeatureFlag); shaderCaps->fDualSourceBlendingSupport = SkToBool(featureFlags & kDualSrcBlend_GrVkFeatureFlag); if (kAMD_VkVendor == properties.vendorID) { // Currently DualSourceBlending is not working on AMD. vkCreateGraphicsPipeline fails when // using a draw with dual source. Looking into whether it is driver bug or issue with our // SPIR-V. Bug skia:6405 shaderCaps->fDualSourceBlendingSupport = false; } shaderCaps->fIntegerSupport = true; shaderCaps->fTexelBufferSupport = true; shaderCaps->fTexelFetchSupport = true; shaderCaps->fVertexIDSupport = true; // Assume the minimum precisions mandated by the SPIR-V spec. shaderCaps->fShaderPrecisionVaries = true; for (int s = 0; s < kGrShaderTypeCount; ++s) { auto& highp = shaderCaps->fFloatPrecisions[s][kHigh_GrSLPrecision]; highp.fLogRangeLow = highp.fLogRangeHigh = 127; highp.fBits = 23; auto& mediump = shaderCaps->fFloatPrecisions[s][kMedium_GrSLPrecision]; mediump.fLogRangeLow = mediump.fLogRangeHigh = 14; mediump.fBits = 10; shaderCaps->fFloatPrecisions[s][kLow_GrSLPrecision] = mediump; } shaderCaps->initSamplerPrecisionTable(); shaderCaps->fMaxVertexSamplers = shaderCaps->fMaxGeometrySamplers = shaderCaps->fMaxFragmentSamplers = SkTMin( SkTMin(properties.limits.maxPerStageDescriptorSampledImages, properties.limits.maxPerStageDescriptorSamplers), (uint32_t)INT_MAX); shaderCaps->fMaxCombinedSamplers = SkTMin( SkTMin(properties.limits.maxDescriptorSetSampledImages, properties.limits.maxDescriptorSetSamplers), (uint32_t)INT_MAX); }