blob: dbd261d140c8f4a87f1dbed12681776dea0f80eb [file] [log] [blame]
/*
* 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 "src/gpu/graphite/precompile/PaintOption.h"
#include "include/core/SkBlender.h"
#include "include/gpu/graphite/precompile/PrecompileBlender.h"
#include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
#include "include/gpu/graphite/precompile/PrecompileShader.h"
#include "src/core/SkBlendModePriv.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/PaintParams.h"
#include "src/gpu/graphite/PaintParamsKey.h"
#include "src/gpu/graphite/PrecompileInternal.h"
#include "src/gpu/graphite/precompile/PrecompileBasePriv.h"
#include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h"
#include "src/gpu/graphite/precompile/PrecompileShaderPriv.h"
namespace skgpu::graphite {
PaintOption::PaintOption(bool opaquePaintColor,
const std::pair<sk_sp<PrecompileBlender>, int>& finalBlender,
const std::pair<sk_sp<PrecompileShader>, int>& shader,
const std::pair<sk_sp<PrecompileColorFilter>, int>& colorFilter,
bool hasPrimitiveBlender,
SkBlendMode primitiveBlendMode,
bool skipColorXform,
const std::pair<sk_sp<PrecompileShader>, int>& clipShader,
Coverage coverage,
TextureFormat targetFormat,
bool dither,
bool analyticClip)
: fOpaquePaintColor(opaquePaintColor)
, fFinalBlender(finalBlender)
, fShader(shader)
, fColorFilter(colorFilter)
, fPrimitiveBlendMode(primitiveBlendMode)
, fHasPrimitiveBlender(hasPrimitiveBlender)
, fSkipColorXform(skipColorXform)
, fClipShader(clipShader)
, fRendererCoverage(coverage)
, fTargetFormat(targetFormat)
, fDither(dither)
, fAnalyticClip(analyticClip) {
if (!fHasPrimitiveBlender) {
if (fShader.first && fShader.first->priv().isConstant(fShader.second)) {
fShader = { nullptr, 0 };
}
if (!fShader.first && fColorFilter.first) {
fColorFilter = { nullptr, 0 };
}
}
}
void PaintOption::toKey(const KeyContext& keyContext) const {
// Root Node 0 is the source color, which is the output of all effects post dithering
this->handleDithering(keyContext);
// Root Node 1 is the final blender
std::optional<SkBlendMode> finalBlendMode =
this->finalBlender() ? this->finalBlender()->priv().asBlendMode()
: SkBlendMode::kSrcOver;
Coverage finalCoverage = fRendererCoverage;
if ((fClipShader.first || fAnalyticClip) && fRendererCoverage == Coverage::kNone) {
finalCoverage = Coverage::kSingleChannel;
}
bool dstReadReq = !finalBlendMode.has_value() ||
!CanUseHardwareBlending(keyContext.caps(),
fTargetFormat,
*finalBlendMode,
finalCoverage);
if (finalBlendMode) {
if (!dstReadReq) {
AddFixedBlendMode(keyContext, *finalBlendMode);
} else {
AddBlendMode(keyContext, *finalBlendMode);
}
} else {
SkASSERT(this->finalBlender());
fFinalBlender.first->priv().addToKey(keyContext, fFinalBlender.second);
}
// Optional Root Node 2 is the clip
this->handleClipping(keyContext);
}
void PaintOption::addPaintColorToKey(const KeyContext& keyContext) const {
if (fShader.first) {
fShader.first->priv().addToKey(keyContext, fShader.second);
} else {
RGBPaintColorBlock::AddBlock(keyContext);
}
}
void PaintOption::handlePrimitiveColor(const KeyContext& keyContext) const {
if (!fHasPrimitiveBlender) {
this->addPaintColorToKey(keyContext);
return;
}
if (fSkipColorXform && fPrimitiveBlendMode == SkBlendMode::kDst) {
AddPrimitiveColor(keyContext, fSkipColorXform);
return;
}
Blend(keyContext,
/* addBlendToKey= */ [&] () -> void {
/**
* TODO: Allow clients to provide precompile SkBlender options for primitive
* blending. For now we have a back door to internally specify an SkBlendMode.
*/
AddToKey(keyContext, GetBlendModeSingleton(fPrimitiveBlendMode));
},
/* addSrcToKey= */ [&]() -> void {
this->addPaintColorToKey(keyContext);
},
/* addDstToKey= */ [&]() -> void {
AddPrimitiveColor(keyContext, fSkipColorXform);
});
}
void PaintOption::handlePaintAlpha(const KeyContext& keyContext) const {
if (!fShader.first && !fHasPrimitiveBlender) {
// If there is no shader and no primitive blending the input to the colorFilter stage
// is just the premultiplied paint color.
SolidColorShaderBlock::AddBlock(keyContext, SK_PMColor4fWHITE);
return;
}
if (!fOpaquePaintColor) {
Blend(keyContext,
/* addBlendToKey= */ [&] () -> void {
AddFixedBlendMode(keyContext, SkBlendMode::kSrcIn);
},
/* addSrcToKey= */ [&]() -> void {
this->handlePrimitiveColor(keyContext);
},
/* addDstToKey= */ [&]() -> void {
AlphaOnlyPaintColorBlock::AddBlock(keyContext);
});
} else {
this->handlePrimitiveColor(keyContext);
}
}
void PaintOption::handleColorFilter(const KeyContext& keyContext) const {
if (fColorFilter.first) {
Compose(keyContext,
/* addInnerToKey= */ [&]() -> void {
this->handlePaintAlpha(keyContext);
},
/* addOuterToKey= */ [&]() -> void {
fColorFilter.first->priv().addToKey(keyContext, fColorFilter.second);
});
} else {
this->handlePaintAlpha(keyContext);
}
}
// This should be kept in sync w/ SkPaintPriv::ShouldDither and PaintParams::should_dither
bool PaintOption::shouldDither(SkColorType dstCT) const {
// The paint dither flag can veto.
if (!fDither) {
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 fShader.first && !fShader.first->priv().isConstant(fShader.second);
}
void PaintOption::handleDithering(const KeyContext& keyContext) const {
#ifndef SK_IGNORE_GPU_DITHER
SkColorType ct = keyContext.dstColorInfo().colorType();
if (this->shouldDither(ct)) {
Compose(keyContext,
/* addInnerToKey= */ [&]() -> void {
this->handleColorFilter(keyContext);
},
/* addOuterToKey= */ [&]() -> void {
AddDitherBlock(keyContext, ct);
});
} else
#endif
{
this->handleColorFilter(keyContext);
}
}
void PaintOption::handleClipping(const KeyContext& keyContext) const {
if (fAnalyticClip) {
NonMSAAClipBlock::NonMSAAClipData data(
/* rect= */ {},
/* radiusPlusHalf= */ {},
/* edgeSelect= */ {},
/* texCoordOffset= */ {},
/* maskBounds= */ {},
// TODO: the kAnalyticAndAtlasClip vs. kAnalyticClip decision is based on this
// being a valid TextureProxy.
/* atlasTexture= */ nullptr);
if (fClipShader.first) {
// For both an analytic clip and clip shader, we need to compose them together into
// a single clipping root node.
Blend(keyContext,
/* addBlendToKey= */ [&]() -> void {
AddFixedBlendMode(keyContext, SkBlendMode::kModulate);
},
/* addSrcToKey= */ [&]() -> void {
NonMSAAClipBlock::AddBlock(keyContext, data);
},
/* addDstToKey= */ [&]() -> void {
fClipShader.first->priv().addToKey(keyContext, fClipShader.second);
});
} else {
// Without a clip shader, the analytic clip can be the clipping root node.
NonMSAAClipBlock::AddBlock(keyContext, data);
}
} else if (fClipShader.first) {
// Since there's no analytic clip, the clipping root node can be fClipShader directly.
fClipShader.first->priv().addToKey(keyContext, fClipShader.second);
}
}
} // namespace skgpu::graphite