/* * Copyright 2024 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/gpu/graphite/precompile/PaintOptions.h" #include "include/gpu/graphite/precompile/PrecompileBlender.h" #include "include/gpu/graphite/precompile/PrecompileColorFilter.h" #include "include/gpu/graphite/precompile/PrecompileImageFilter.h" #include "include/gpu/graphite/precompile/PrecompileMaskFilter.h" #include "include/gpu/graphite/precompile/PrecompileShader.h" #include "src/gpu/graphite/Caps.h" #include "src/gpu/graphite/ContextUtils.h" #include "src/gpu/graphite/KeyContext.h" #include "src/gpu/graphite/PaintParamsKey.h" #include "src/gpu/graphite/PrecompileInternal.h" #include "src/gpu/graphite/Renderer.h" #include "src/gpu/graphite/ShaderCodeDictionary.h" #include "src/gpu/graphite/precompile/PaintOption.h" #include "src/gpu/graphite/precompile/PaintOptionsPriv.h" #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h" #include "src/gpu/graphite/precompile/PrecompileBasePriv.h" #include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h" #include "src/gpu/graphite/precompile/PrecompileShaderPriv.h" #include "src/gpu/graphite/precompile/PrecompileShadersPriv.h" namespace skgpu::graphite { PaintOptions::PaintOptions() = default; PaintOptions::PaintOptions(const PaintOptions&) = default; PaintOptions::~PaintOptions() = default; PaintOptions& PaintOptions::operator=(const PaintOptions&) = default; void PaintOptions::setShaders(SkSpan> shaders) { fShaderOptions.clear(); fShaderOptions.push_back_n(shaders.size(), shaders.data()); } void PaintOptions::setImageFilters(SkSpan> imageFilters) { fImageFilterOptions.clear(); fImageFilterOptions.push_back_n(imageFilters.size(), imageFilters.data()); } void PaintOptions::setMaskFilters(SkSpan> maskFilters) { fMaskFilterOptions.clear(); fMaskFilterOptions.push_back_n(maskFilters.size(), maskFilters.data()); } void PaintOptions::setColorFilters(SkSpan> colorFilters) { fColorFilterOptions.clear(); fColorFilterOptions.push_back_n(colorFilters.size(), colorFilters.data()); } void PaintOptions::addColorFilter(sk_sp cf) { fColorFilterOptions.push_back(std::move(cf)); } void PaintOptions::setBlendModes(SkSpan blendModes) { fBlendModeOptions.clear(); fBlendModeOptions.push_back_n(blendModes.size(), blendModes.data()); } void PaintOptions::setBlenders(SkSpan> blenders) { for (const sk_sp& b: blenders) { if (b->priv().asBlendMode().has_value()) { fBlendModeOptions.push_back(b->priv().asBlendMode().value()); } else { fBlenderOptions.push_back(b); } } } void PaintOptions::setClipShaders(SkSpan> clipShaders) { // In the normal API this modification happens in SkDevice::clipShader() fClipShaderOptions.reserve(2 * clipShaders.size()); for (const sk_sp& cs : clipShaders) { // All clipShaders get wrapped in a CTMShader ... sk_sp withCTM = cs ? PrecompileShadersPriv::CTM({ cs }) : nullptr; // and, if it is a SkClipOp::kDifference clip, an additional ColorFilterShader sk_sp inverted = withCTM ? withCTM->makeWithColorFilter(PrecompileColorFilters::Blend()) : nullptr; fClipShaderOptions.emplace_back(std::move(withCTM)); fClipShaderOptions.emplace_back(std::move(inverted)); } } int PaintOptions::numShaderCombinations() const { int numShaderCombinations = 0; for (const sk_sp& s : fShaderOptions) { numShaderCombinations += s->numCombinations(); } // If no shader option is specified we will add a solid color shader option return numShaderCombinations ? numShaderCombinations : 1; } int PaintOptions::numColorFilterCombinations() const { int numColorFilterCombinations = 0; for (const sk_sp& cf : fColorFilterOptions) { if (!cf) { ++numColorFilterCombinations; } else { numColorFilterCombinations += cf->numCombinations(); } } // If no color filter options are specified we will use the unmodified result color return numColorFilterCombinations ? numColorFilterCombinations : 1; } int PaintOptions::numBlendCombinations() const { int numBlendCombos = fBlendModeOptions.size(); for (const sk_sp& b: fBlenderOptions) { SkASSERT(!b->priv().asBlendMode().has_value()); numBlendCombos += b->priv().numChildCombinations(); } if (!numBlendCombos) { // If the user didn't specify a blender we will fall back to kSrcOver blending numBlendCombos = 1; } return numBlendCombos; } int PaintOptions::numClipShaderCombinations() const { int numClipShaderCombos = 0; for (const sk_sp& cs: fClipShaderOptions) { if (cs) { numClipShaderCombos += cs->priv().numChildCombinations(); } else { ++numClipShaderCombos; } } // If no clipShader options are specified we will just have the unclipped options return numClipShaderCombos ? numClipShaderCombos : 1; } int PaintOptions::numCombinations() const { return this->numShaderCombinations() * this->numColorFilterCombinations() * this->numBlendCombinations() * this->numClipShaderCombinations(); } namespace { DstReadRequirement get_dst_read_req(const Caps* caps, Coverage coverage, PrecompileBlender* blender) { if (blender) { return GetDstReadRequirement(caps, blender->priv().asBlendMode(), coverage); } return GetDstReadRequirement(caps, SkBlendMode::kSrcOver, coverage); } } // anonymous namespace void PaintOptions::createKey(const KeyContext& keyContext, PaintParamsKeyBuilder* keyBuilder, PipelineDataGatherer* gatherer, int desiredCombination, bool addPrimitiveBlender, Coverage coverage) const { SkDEBUGCODE(keyBuilder->checkReset();) SkASSERT(desiredCombination < this->numCombinations()); const int numClipShaderCombos = this->numClipShaderCombinations(); const int numBlendModeCombos = this->numBlendCombinations(); const int numColorFilterCombinations = this->numColorFilterCombinations(); const int desiredClipShaderCombination = desiredCombination % numClipShaderCombos; int remainingCombinations = desiredCombination / numClipShaderCombos; const int desiredBlendCombination = remainingCombinations % numBlendModeCombos; remainingCombinations /= numBlendModeCombos; const int desiredColorFilterCombination = remainingCombinations % numColorFilterCombinations; remainingCombinations /= numColorFilterCombinations; const int desiredShaderCombination = remainingCombinations; SkASSERT(desiredShaderCombination < this->numShaderCombinations()); // TODO: this probably needs to be passed in just like addPrimitiveBlender const bool kOpaquePaintColor = true; auto clipShader = PrecompileBase::SelectOption(SkSpan(fClipShaderOptions), desiredClipShaderCombination); std::pair, int> finalBlender; if (desiredBlendCombination < fBlendModeOptions.size()) { finalBlender = { PrecompileBlenders::Mode(fBlendModeOptions[desiredBlendCombination]), 0 }; } else { finalBlender = PrecompileBase::SelectOption( SkSpan(fBlenderOptions), desiredBlendCombination - fBlendModeOptions.size()); } if (!finalBlender.first) { finalBlender = { PrecompileBlenders::Mode(SkBlendMode::kSrcOver), 0 }; } DstReadRequirement dstReadReq = get_dst_read_req(keyContext.caps(), coverage, finalBlender.first.get()); PaintOption option(kOpaquePaintColor, finalBlender, PrecompileBase::SelectOption(SkSpan(fShaderOptions), desiredShaderCombination), PrecompileBase::SelectOption(SkSpan(fColorFilterOptions), desiredColorFilterCombination), addPrimitiveBlender, clipShader, dstReadReq, fDither); option.toKey(keyContext, keyBuilder, gatherer); } namespace { void create_image_drawing_pipelines(const KeyContext& keyContext, PipelineDataGatherer* gatherer, const PaintOptions& orig, const RenderPassDesc& renderPassDesc, const PaintOptionsPriv::ProcessCombination& processCombination) { PaintOptions imagePaintOptions; // For imagefilters we know we don't have alpha-only textures and don't need cubic filtering. sk_sp imageShader = PrecompileShadersPriv::Image( PrecompileImageShaderFlags::kExcludeAlpha | PrecompileImageShaderFlags::kExcludeCubic); imagePaintOptions.setShaders({ imageShader }); imagePaintOptions.setBlendModes(orig.getBlendModes()); imagePaintOptions.setBlenders(orig.getBlenders()); imagePaintOptions.setColorFilters(orig.getColorFilters()); imagePaintOptions.priv().addColorFilter(nullptr); imagePaintOptions.priv().buildCombinations(keyContext, gatherer, DrawTypeFlags::kSimpleShape, /* withPrimitiveBlender= */ false, Coverage::kSingleChannel, renderPassDesc, processCombination); } } // anonymous namespace void PaintOptions::buildCombinations( const KeyContext& keyContext, PipelineDataGatherer* gatherer, DrawTypeFlags drawTypes, bool withPrimitiveBlender, Coverage coverage, const RenderPassDesc& renderPassDesc, const ProcessCombination& processCombination) const { PaintParamsKeyBuilder builder(keyContext.dict()); if (!fImageFilterOptions.empty() || !fMaskFilterOptions.empty()) { // TODO: split this out into a create_restore_draw_pipelines method PaintOptions tmp = *this; // When image filtering, the original blend mode is taken over by the restore paint tmp.setImageFilters({}); tmp.setMaskFilters({}); tmp.addBlendMode(SkBlendMode::kSrcOver); if (!fImageFilterOptions.empty()) { std::vector> newCFs(tmp.fColorFilterOptions.begin(), tmp.fColorFilterOptions.end()); if (newCFs.empty()) { // TODO: I (robertphillips) believe this is unnecessary and is just a result of the // base SkPaint generated in the PaintParamsKeyTest not correctly taking CFIFs into // account. newCFs.push_back(nullptr); } // As in SkCanvasPriv::ImageToColorFilter, we fuse CFIFs into the base draw's CFs. // TODO: in SkCanvasPriv::ImageToColorFilter this fusing of CFIFs and CFs is skipped // when there is a maskfilter. For now we over-generate. for (const sk_sp& o : fImageFilterOptions) { sk_sp imageFiltersCF = o ? o->asAColorFilter() : nullptr; if (imageFiltersCF) { if (!tmp.fColorFilterOptions.empty()) { for (const sk_sp& cf : tmp.fColorFilterOptions) { // TODO: if a CFIF was fully handled here it should be removed from the // later loop over fImageFilterOptions. For now we over-generate. sk_sp newCF = imageFiltersCF->makeComposed(cf); newCFs.push_back(std::move(newCF)); } } else { newCFs.push_back(imageFiltersCF); } } } tmp.setColorFilters(newCFs); } tmp.buildCombinations(keyContext, gatherer, drawTypes, withPrimitiveBlender, coverage, renderPassDesc, processCombination); create_image_drawing_pipelines(keyContext, gatherer, *this, renderPassDesc, processCombination); for (const sk_sp& o : fImageFilterOptions) { o->createPipelines(keyContext, gatherer, renderPassDesc, processCombination); } for (const sk_sp& o : fMaskFilterOptions) { o->createPipelines(keyContext, gatherer, *this, renderPassDesc, processCombination); } } else { int numCombinations = this->numCombinations(); for (int i = 0; i < numCombinations; ++i) { // Since the precompilation path's uniforms aren't used and don't change the key, // the exact layout doesn't matter gatherer->resetWithNewLayout(Layout::kMetal); this->createKey(keyContext, &builder, gatherer, i, withPrimitiveBlender, coverage); // The 'findOrCreate' calls lockAsKey on builder and then destroys the returned // PaintParamsKey. This serves to reset the builder. UniquePaintParamsID paintID = keyContext.dict()->findOrCreate(&builder); processCombination(paintID, drawTypes, withPrimitiveBlender, coverage, renderPassDesc); } } } void PaintOptionsPriv::addColorFilter(sk_sp cf) { fPaintOptions->addColorFilter(std::move(cf)); } } // namespace skgpu::graphite