/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/graphite/PaintParams.h" #include "include/core/SkColorSpace.h" #include "include/core/SkShader.h" #include "src/core/SkBlendModeBlender.h" #include "src/core/SkBlenderBase.h" #include "src/core/SkColorSpacePriv.h" #include "src/effects/colorfilters/SkColorFilterBase.h" #include "src/gpu/Blend.h" #include "src/gpu/DitherUtils.h" #include "src/gpu/graphite/KeyContext.h" #include "src/gpu/graphite/KeyHelpers.h" #include "src/gpu/graphite/Log.h" #include "src/gpu/graphite/PaintParamsKey.h" #include "src/gpu/graphite/PipelineData.h" #include "src/gpu/graphite/RecorderPriv.h" #include "src/gpu/graphite/Uniform.h" #include "src/shaders/SkShaderBase.h" namespace skgpu::graphite { namespace { // This should be kept in sync w/ SkPaintPriv::ShouldDither and PaintOption::shouldDither bool should_dither(const PaintParams& p, SkColorType dstCT) { // The paint dither flag can veto. if (!p.dither()) { return false; } if (dstCT == kUnknown_SkColorType) { return false; } // We always dither 565 or 4444 when requested. if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) { return true; } // Otherwise, dither is only needed for non-const paints. return p.shader() && !as_SB(p.shader())->isConstant(); } } // anonymous namespace PaintParams::PaintParams(const SkPaint& paint, sk_sp primitiveBlender, sk_sp clipShader, DstReadRequirement dstReadReq, bool skipColorXform) : fColor(paint.getColor4f()) , fFinalBlender(paint.refBlender()) , fShader(paint.refShader()) , fColorFilter(paint.refColorFilter()) , fPrimitiveBlender(std::move(primitiveBlender)) , fClipShader(std::move(clipShader)) , fDstReadReq(dstReadReq) , fSkipColorXform(skipColorXform) , fDither(paint.isDither()) {} PaintParams::PaintParams(const PaintParams& other) = default; PaintParams::~PaintParams() = default; PaintParams& PaintParams::operator=(const PaintParams& other) = default; std::optional PaintParams::asFinalBlendMode() const { return fFinalBlender ? as_BB(fFinalBlender)->asBlendMode() : SkBlendMode::kSrcOver; } sk_sp PaintParams::refFinalBlender() const { return fFinalBlender; } sk_sp PaintParams::refShader() const { return fShader; } sk_sp PaintParams::refColorFilter() const { return fColorFilter; } sk_sp PaintParams::refPrimitiveBlender() const { return fPrimitiveBlender; } SkColor4f PaintParams::Color4fPrepForDst(SkColor4f srcColor, const SkColorInfo& dstColorInfo) { // xform from sRGB to the destination colorspace SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType, dstColorInfo.colorSpace(), kUnpremul_SkAlphaType); SkColor4f result = srcColor; steps.apply(result.vec()); return result; } void Blend(const KeyContext& keyContext, PaintParamsKeyBuilder* keyBuilder, PipelineDataGatherer* gatherer, AddToKeyFn addBlendToKey, AddToKeyFn addSrcToKey, AddToKeyFn addDstToKey) { BlendShaderBlock::BeginBlock(keyContext, keyBuilder, gatherer); addSrcToKey(); addDstToKey(); addBlendToKey(); keyBuilder->endBlock(); // BlendShaderBlock } void Compose(const KeyContext& keyContext, PaintParamsKeyBuilder* keyBuilder, PipelineDataGatherer* gatherer, AddToKeyFn addInnerToKey, AddToKeyFn addOuterToKey) { ComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer); addInnerToKey(); addOuterToKey(); keyBuilder->endBlock(); // ComposeBlock } void AddKnownModeBlend(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, SkBlendMode bm) { SkASSERT(bm <= SkBlendMode::kLastCoeffMode); BuiltInCodeSnippetID id = static_cast(kFixedFunctionBlendModeIDOffset + static_cast(bm)); builder->addBlock(id); } void AddModeBlend(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, SkBlendMode bm) { SkSpan coeffs = skgpu::GetPorterDuffBlendConstants(bm); if (!coeffs.empty()) { CoeffBlenderBlock::AddBlock(keyContext, builder, gatherer, coeffs); } else { BlendModeBlenderBlock::AddBlock(keyContext, builder, gatherer, bm); } } void AddDstReadBlock(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, DstReadRequirement dstReadReq) { switch(dstReadReq) { case DstReadRequirement::kNone: SkASSERT(false); // This should never be reached return; case DstReadRequirement::kTextureCopy: [[fallthrough]]; case DstReadRequirement::kTextureSample: DstReadSampleBlock::AddBlock(keyContext, builder, gatherer, keyContext.dstTexture(), keyContext.dstOffset()); break; case DstReadRequirement::kFramebufferFetch: builder->addBlock(BuiltInCodeSnippetID::kDstReadFetch); break; } } void AddDitherBlock(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, SkColorType ct) { static const SkBitmap gLUT = skgpu::MakeDitherLUT(); sk_sp proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), gLUT, "DitherLUT"); if (keyContext.recorder() && !proxy) { SKGPU_LOG_W("Couldn't create dither shader's LUT"); builder->addBlock(BuiltInCodeSnippetID::kPriorOutput); return; } DitherShaderBlock::DitherData data(skgpu::DitherRangeForConfig(ct), std::move(proxy)); DitherShaderBlock::AddBlock(keyContext, builder, gatherer, data); } void PaintParams::addPaintColorToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* keyBuilder, PipelineDataGatherer* gatherer) const { if (fShader) { AddToKey(keyContext, keyBuilder, gatherer, fShader.get()); } else { RGBPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer); } } /** * Primitive blend blocks are used to blend either the paint color or the output of another shader * with a primitive color emitted by certain draw geometry calls (drawVertices, drawAtlas, etc.). * Dst: primitiveColor Src: Paint color/shader output */ void PaintParams::handlePrimitiveColor(const KeyContext& keyContext, PaintParamsKeyBuilder* keyBuilder, PipelineDataGatherer* gatherer) const { if (fPrimitiveBlender) { Blend(keyContext, keyBuilder, gatherer, /* addBlendToKey= */ [&] () -> void { AddToKey(keyContext, keyBuilder, gatherer, fPrimitiveBlender.get()); }, /* addSrcToKey= */ [&]() -> void { this->addPaintColorToKey(keyContext, keyBuilder, gatherer); }, /* addDstToKey= */ [&]() -> void { keyBuilder->addBlock(BuiltInCodeSnippetID::kPrimitiveColor); }); } else { this->addPaintColorToKey(keyContext, keyBuilder, gatherer); } } // Apply the paint's alpha value. void PaintParams::handlePaintAlpha(const KeyContext& keyContext, PaintParamsKeyBuilder* keyBuilder, PipelineDataGatherer* gatherer) const { if (!fShader && !fPrimitiveBlender) { // If there is no shader and no primitive blending the input to the colorFilter stage // is just the premultiplied paint color. SkPMColor4f paintColor = PaintParams::Color4fPrepForDst(fColor, keyContext.dstColorInfo()).premul(); SolidColorShaderBlock::AddBlock(keyContext, keyBuilder, gatherer, paintColor); return; } if (fColor.fA != 1.0f) { Blend(keyContext, keyBuilder, gatherer, /* addBlendToKey= */ [&] () -> void { AddKnownModeBlend(keyContext, keyBuilder, gatherer, SkBlendMode::kSrcIn); }, /* addSrcToKey= */ [&]() -> void { this->handlePrimitiveColor(keyContext, keyBuilder, gatherer); }, /* addDstToKey= */ [&]() -> void { AlphaOnlyPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer); }); } else { this->handlePrimitiveColor(keyContext, keyBuilder, gatherer); } } void PaintParams::handleColorFilter(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer) const { if (fColorFilter) { Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { this->handlePaintAlpha(keyContext, builder, gatherer); }, /* addOuterToKey= */ [&]() -> void { AddToKey(keyContext, builder, gatherer, fColorFilter.get()); }); } else { this->handlePaintAlpha(keyContext, builder, gatherer); } } void PaintParams::handleDithering(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer) const { #ifndef SK_IGNORE_GPU_DITHER SkColorType ct = keyContext.dstColorInfo().colorType(); if (should_dither(*this, ct)) { Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { this->handleColorFilter(keyContext, builder, gatherer); }, /* addOuterToKey= */ [&]() -> void { AddDitherBlock(keyContext, builder, gatherer, ct); }); } else #endif { this->handleColorFilter(keyContext, builder, gatherer); } } void PaintParams::handleDstRead(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer) const { if (fDstReadReq != DstReadRequirement::kNone) { Blend(keyContext, builder, gatherer, /* addBlendToKey= */ [&] () -> void { if (fFinalBlender) { AddToKey(keyContext, builder, gatherer, fFinalBlender.get()); } else { AddKnownModeBlend(keyContext, builder, gatherer, SkBlendMode::kSrcOver); } }, /* addSrcToKey= */ [&]() -> void { this->handleDithering(keyContext, builder, gatherer); }, /* addDstToKey= */ [&]() -> void { AddDstReadBlock(keyContext, builder, gatherer, fDstReadReq); }); } else { this->handleDithering(keyContext, builder, gatherer); } } void PaintParams::toKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer) const { this->handleDstRead(keyContext, builder, gatherer); std::optional finalBlendMode = this->asFinalBlendMode(); if (fDstReadReq != DstReadRequirement::kNone) { // In this case the blend will have been handled by shader-based blending with the dstRead. finalBlendMode = SkBlendMode::kSrc; } if (fClipShader) { ClipShaderBlock::BeginBlock(keyContext, builder, gatherer); AddToKey(keyContext, builder, gatherer, fClipShader.get()); builder->endBlock(); } // Set the hardware blend mode. SkASSERT(finalBlendMode); BuiltInCodeSnippetID fixedFuncBlendModeID = static_cast( kFixedFunctionBlendModeIDOffset + static_cast(*finalBlendMode)); builder->addBlock(fixedFuncBlendModeID); } // TODO(b/330864257): Can be deleted once keys are determined by the Device draw. void PaintParams::notifyImagesInUse(Recorder* recorder, DrawContext* drawContext) const { if (fShader) { NotifyImagesInUse(recorder, drawContext, fShader.get()); } if (fPrimitiveBlender) { NotifyImagesInUse(recorder, drawContext, fPrimitiveBlender.get()); } if (fColorFilter) { NotifyImagesInUse(recorder, drawContext, fColorFilter.get()); } if (fFinalBlender) { NotifyImagesInUse(recorder, drawContext, fFinalBlender.get()); } if (fClipShader) { NotifyImagesInUse(recorder, drawContext, fClipShader.get()); } } } // namespace skgpu::graphite