blob: e1c3be56cd3e2ed5b753be1369b4565cc505dc38 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/ganesh/Device.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkClipOp.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkColorType.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkM44.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkMesh.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkRegion.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSize.h"
#include "include/core/SkSpan.h"
#include "include/core/SkStrokeRec.h"
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkVertices.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrRecordingContext.h"
#include "include/gpu/GrTypes.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "include/private/SkColorData.h"
#include "include/private/base/SingleOwner.h"
#include "include/private/base/SkAssert.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkTArray.h"
#include "include/private/base/SkTo.h"
#include "include/private/chromium/Slug.h" // IWYU pragma: keep
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/base/SkTLazy.h"
#include "src/core/SkDevice.h"
#include "src/core/SkDrawBase.h"
#include "src/core/SkImageFilterTypes.h" // IWYU pragma: keep
#include "src/core/SkImageInfoPriv.h"
#include "src/core/SkLatticeIter.h"
#include "src/core/SkMeshPriv.h"
#include "src/core/SkRasterClip.h"
#include "src/core/SkSpecialImage.h"
#include "src/core/SkStrikeCache.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkVerticesPriv.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/TiledTextureUtils.h"
#include "src/gpu/ganesh/ClipStack.h"
#include "src/gpu/ganesh/GrAuditTrail.h"
#include "src/gpu/ganesh/GrBlurUtils.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrColorSpaceXform.h"
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrImageInfo.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrRenderTargetProxy.h"
#include "src/gpu/ganesh/GrStyle.h"
#include "src/gpu/ganesh/GrSurfaceProxy.h"
#include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/GrTextureProxy.h"
#include "src/gpu/ganesh/GrTracing.h"
#include "src/gpu/ganesh/GrUserStencilSettings.h"
#include "src/gpu/ganesh/GrXferProcessor.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/SurfaceContext.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
#include "src/gpu/ganesh/SurfaceFillContext.h"
#include "src/gpu/ganesh/effects/GrDisableColorXP.h"
#include "src/gpu/ganesh/effects/GrRRectEffect.h"
#include "src/gpu/ganesh/geometry/GrShape.h"
#include "src/gpu/ganesh/geometry/GrStyledShape.h"
#include "src/gpu/ganesh/image/GrImageUtils.h"
#include "src/gpu/ganesh/image/SkSpecialImage_Ganesh.h"
#include "src/text/GlyphRun.h"
#include "src/text/gpu/SlugImpl.h"
#include "src/text/gpu/SubRunContainer.h"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <tuple>
#include <utility>
class GrBackendSemaphore;
struct GrShaderCaps;
struct SkDrawShadowRec;
using namespace skia_private;
#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(fContext->priv().singleOwner())
#if defined(GR_TEST_UTILS)
// GrContextOptions::fMaxTextureSizeOverride exists but doesn't allow for changing the
// maxTextureSize on the fly.
int gOverrideMaxTextureSizeGanesh = 0;
// Allows tests to check how many tiles were drawn on the most recent call to
// Device::drawAsTiledImageRect. This is an atomic because we can write to it from
// multiple threads during "normal" operations. However, the tests that actually
// read from it are done single-threaded.
std::atomic<int> gNumTilesDrawnGanesh{0};
#endif
///////////////////////////////////////////////////////////////////////////////
namespace {
bool force_aa_clip(const skgpu::ganesh::SurfaceDrawContext* sdc) {
return sdc->numSamples() > 1 || sdc->alwaysAntialias();
}
inline GrPrimitiveType point_mode_to_primitive_type(SkCanvas::PointMode mode) {
switch (mode) {
case SkCanvas::kPoints_PointMode:
return GrPrimitiveType::kPoints;
case SkCanvas::kLines_PointMode:
return GrPrimitiveType::kLines;
case SkCanvas::kPolygon_PointMode:
return GrPrimitiveType::kLineStrip;
}
SK_ABORT("Unexpected mode");
}
std::unique_ptr<GrFragmentProcessor> make_inverse_rrect_fp(const SkMatrix& viewMatrix,
const SkRRect& rrect, GrAA aa,
const GrShaderCaps& shaderCaps) {
SkTCopyOnFirstWrite<SkRRect> devRRect(rrect);
if (viewMatrix.isIdentity() || rrect.transform(viewMatrix, devRRect.writable())) {
auto edgeType = (aa == GrAA::kYes) ? GrClipEdgeType::kInverseFillAA
: GrClipEdgeType::kInverseFillBW;
auto [success, fp] = GrRRectEffect::Make(/*inputFP=*/nullptr, edgeType, *devRRect,
shaderCaps);
return (success) ? std::move(fp) : nullptr;
}
return nullptr;
}
bool init_vertices_paint(GrRecordingContext* rContext,
const GrColorInfo& colorInfo,
const SkPaint& skPaint,
const SkMatrix& ctm,
SkBlender* blender,
bool hasColors,
const SkSurfaceProps& props,
GrPaint* grPaint) {
if (hasColors) {
return SkPaintToGrPaintWithBlend(rContext,
colorInfo,
skPaint,
ctm,
blender,
props,
grPaint);
} else {
return SkPaintToGrPaint(rContext, colorInfo, skPaint, ctm, props, grPaint);
}
}
bool init_mesh_child_effects(GrRecordingContext* rContext,
const GrColorInfo& colorInfo,
const SkSurfaceProps& surfaceProps,
const SkMesh& mesh,
TArray<std::unique_ptr<GrFragmentProcessor>>* meshChildFPs) {
// We use `Scope::kRuntimeEffect` here to ensure that mesh shaders get the same "raw" sampling
// behavior from alpha-only image shaders as a Runtime Effect would, rather than the unexpected
// tinting-by-input-color.
GrFPArgs fpArgs(rContext, &colorInfo, surfaceProps, GrFPArgs::Scope::kRuntimeEffect);
for (const SkRuntimeEffect::ChildPtr& child : mesh.children()) {
auto [success, childFP] = GrFragmentProcessors::MakeChildFP(child, fpArgs);
if (!success) {
return false;
}
meshChildFPs->push_back(std::move(childFP));
}
return true;
}
} // anonymous namespace
namespace skgpu::ganesh {
sk_sp<Device> Device::Make(GrRecordingContext* rContext,
GrColorType colorType,
sk_sp<GrSurfaceProxy> proxy,
sk_sp<SkColorSpace> colorSpace,
GrSurfaceOrigin origin,
const SkSurfaceProps& surfaceProps,
InitContents init) {
auto sdc = SurfaceDrawContext::Make(rContext,
colorType,
std::move(proxy),
std::move(colorSpace),
origin,
surfaceProps);
return Device::Make(std::move(sdc), kPremul_SkAlphaType, init);
}
SkImageInfo Device::MakeInfo(SurfaceContext* sc, DeviceFlags flags) {
SkColorType colorType = GrColorTypeToSkColorType(sc->colorInfo().colorType());
return SkImageInfo::Make(sc->width(), sc->height(), colorType,
flags & DeviceFlags::kIsOpaque ? kOpaque_SkAlphaType
: kPremul_SkAlphaType,
sc->colorInfo().refColorSpace());
}
/** Checks that the alpha type is legal and gets constructor flags. Returns false if device creation
should fail. */
bool Device::CheckAlphaTypeAndGetFlags(SkAlphaType alphaType,
InitContents init,
DeviceFlags* flags) {
*flags = DeviceFlags::kNone;
switch (alphaType) {
case kPremul_SkAlphaType:
break;
case kOpaque_SkAlphaType:
*flags |= DeviceFlags::kIsOpaque;
break;
default: // If it is unpremul or unknown don't try to render
return false;
}
if (InitContents::kClear == init) {
*flags |= DeviceFlags::kNeedClear;
}
return true;
}
sk_sp<Device> Device::Make(std::unique_ptr<SurfaceDrawContext> sdc,
SkAlphaType alphaType,
InitContents init) {
if (!sdc) {
return nullptr;
}
GrRecordingContext* rContext = sdc->recordingContext();
if (rContext->abandoned()) {
return nullptr;
}
SkColorType ct = GrColorTypeToSkColorType(sdc->colorInfo().colorType());
DeviceFlags flags;
if (!rContext->colorTypeSupportedAsSurface(ct) ||
!CheckAlphaTypeAndGetFlags(alphaType, init, &flags)) {
return nullptr;
}
return sk_sp<Device>(new Device(std::move(sdc), flags));
}
sk_sp<Device> Device::Make(GrRecordingContext* rContext,
skgpu::Budgeted budgeted,
const SkImageInfo& ii,
SkBackingFit fit,
int sampleCount,
skgpu::Mipmapped mipmapped,
GrProtected isProtected,
GrSurfaceOrigin origin,
const SkSurfaceProps& props,
InitContents init) {
if (!rContext) {
return nullptr;
}
auto sdc = SurfaceDrawContext::Make(rContext,
SkColorTypeToGrColorType(ii.colorType()),
ii.refColorSpace(),
fit,
ii.dimensions(),
props,
/*label=*/"MakeDevice",
sampleCount,
mipmapped,
isProtected,
origin,
budgeted);
return Device::Make(std::move(sdc), ii.alphaType(), init);
}
Device::Device(std::unique_ptr<SurfaceDrawContext> sdc, DeviceFlags flags)
: SkDevice(MakeInfo(sdc.get(), flags), sdc->surfaceProps())
, fContext(sk_ref_sp(sdc->recordingContext()))
, fSDFTControl(sdc->recordingContext()->priv().getSDFTControl(
sdc->surfaceProps().isUseDeviceIndependentFonts()))
, fSurfaceDrawContext(std::move(sdc))
, fClip(SkIRect::MakeSize(fSurfaceDrawContext->dimensions()),
&this->localToDevice(),
force_aa_clip(fSurfaceDrawContext.get())) {
if (flags & DeviceFlags::kNeedClear) {
this->clearAll();
}
}
Device::~Device() = default;
///////////////////////////////////////////////////////////////////////////////
bool Device::onReadPixels(const SkPixmap& pm, int x, int y) {
ASSERT_SINGLE_OWNER
// Context TODO: Elevate direct context requirement to public API
auto dContext = fContext->asDirectContext();
if (!dContext || !SkImageInfoValidConversion(pm.info(), this->imageInfo())) {
return false;
}
return fSurfaceDrawContext->readPixels(dContext, pm, {x, y});
}
bool Device::onWritePixels(const SkPixmap& pm, int x, int y) {
ASSERT_SINGLE_OWNER
// Context TODO: Elevate direct context requirement to public API
auto dContext = fContext->asDirectContext();
if (!dContext || !SkImageInfoValidConversion(this->imageInfo(), pm.info())) {
return false;
}
return fSurfaceDrawContext->writePixels(dContext, pm, {x, y});
}
bool Device::onAccessPixels(SkPixmap* pmap) {
ASSERT_SINGLE_OWNER
return false;
}
SurfaceDrawContext* Device::surfaceDrawContext() {
ASSERT_SINGLE_OWNER
return fSurfaceDrawContext.get();
}
const SurfaceDrawContext* Device::surfaceDrawContext() const {
ASSERT_SINGLE_OWNER
return fSurfaceDrawContext.get();
}
SurfaceFillContext* Device::surfaceFillContext() {
ASSERT_SINGLE_OWNER
return fSurfaceDrawContext.get();
}
void Device::clearAll() {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "clearAll", fContext.get());
SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
fSurfaceDrawContext->clearAtLeast(rect, SK_PMColor4fTRANSPARENT);
}
///////////////////////////////////////////////////////////////////////////////
void Device::clipPath(const SkPath& path, SkClipOp op, bool aa) {
#if defined(GR_TEST_UTILS)
if (fContext->priv().options().fAllPathsVolatile && !path.isVolatile()) {
this->clipPath(SkPath(path).setIsVolatile(true), op, aa);
return;
}
#endif
SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
fClip.clipPath(this->localToDevice(), path, GrAA(aa), op);
}
void Device::clipRegion(const SkRegion& globalRgn, SkClipOp op) {
SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
// Regions don't actually need AA, but in DMSAA mode every clip element is antialiased.
GrAA aa = GrAA(fSurfaceDrawContext->alwaysAntialias());
if (globalRgn.isEmpty()) {
fClip.clipRect(SkMatrix::I(), SkRect::MakeEmpty(), aa, op);
} else if (globalRgn.isRect()) {
fClip.clipRect(this->globalToDevice().asM33(), SkRect::Make(globalRgn.getBounds()), aa, op);
} else {
SkPath path;
globalRgn.getBoundaryPath(&path);
fClip.clipPath(this->globalToDevice().asM33(), path, aa, op);
}
}
void Device::android_utils_clipAsRgn(SkRegion* region) const {
SkIRect bounds = fClip.getConservativeBounds();
// Assume wide open and then perform intersect/difference operations reducing the region
region->setRect(bounds);
const SkRegion deviceBounds(bounds);
for (const ClipStack::Element& e : fClip) {
SkRegion tmp;
if (e.fShape.isRect() && e.fLocalToDevice.isIdentity()) {
tmp.setRect(e.fShape.rect().roundOut());
} else {
SkPath tmpPath;
e.fShape.asPath(&tmpPath);
tmpPath.transform(e.fLocalToDevice);
tmp.setPath(tmpPath, deviceBounds);
}
region->op(tmp, (SkRegion::Op) e.fOp);
}
}
bool Device::isClipAntiAliased() const {
for (const ClipStack::Element& e : fClip) {
if (e.fAA == GrAA::kYes) {
return true;
}
SkASSERT(!fSurfaceDrawContext->alwaysAntialias());
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
void Device::drawPaint(const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawPaint", fContext.get());
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawPaint(this->clip(), std::move(grPaint), this->localToDevice());
}
void Device::drawPoints(SkCanvas::PointMode mode,
size_t count,
const SkPoint pts[],
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawPoints", fContext.get());
SkScalar width = paint.getStrokeWidth();
if (width < 0) {
return;
}
GrAA aa = fSurfaceDrawContext->chooseAA(paint);
if (count == 2 && mode == SkCanvas::kLines_PointMode) {
if (paint.getPathEffect()) {
// Probably a dashed line. Draw as a path.
GrPaint grPaint;
if (SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
SkPath path;
path.setIsVolatile(true);
path.moveTo(pts[0]);
path.lineTo(pts[1]);
fSurfaceDrawContext->drawPath(this->clip(),
std::move(grPaint),
aa,
this->localToDevice(),
path,
GrStyle(paint, SkPaint::kStroke_Style));
}
return;
}
if (!paint.getMaskFilter() &&
paint.getStrokeWidth() > 0 && // drawStrokedLine doesn't support hairlines.
paint.getStrokeCap() != SkPaint::kRound_Cap) { // drawStrokedLine doesn't do round caps.
// Simple stroked line. Bypass path rendering.
GrPaint grPaint;
if (SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
fSurfaceDrawContext->drawStrokedLine(this->clip(),
std::move(grPaint),
aa,
this->localToDevice(),
pts,
SkStrokeRec(paint, SkPaint::kStroke_Style));
}
return;
}
}
const GrCaps* caps = fContext->priv().caps();
SkScalar scales[2];
bool isHairline =
((0 == width) ||
(1 == width && this->localToDevice().getMinMaxScales(scales) &&
SkScalarNearlyEqual(scales[0], 1.f) && SkScalarNearlyEqual(scales[1], 1.f))) &&
// Don't do this as a hairline draw, which will emit line primitives, if
// lines are not permitted by caps.
!((mode == SkCanvas::kLines_PointMode || mode == SkCanvas::kPolygon_PointMode) &&
caps->avoidLineDraws());
// we only handle non-coverage-aa hairlines and paints without path effects or mask filters,
// else we let the SkDraw call our drawPath()
if (!isHairline ||
paint.getPathEffect() ||
paint.getMaskFilter() ||
fSurfaceDrawContext->chooseAAType(aa) == GrAAType::kCoverage) {
SkRasterClip rc(this->devClipBounds());
SkDrawBase draw;
// don't need to set fBlitterChoose, as it should never get used
draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(this->width(), this->height()), nullptr, 0);
draw.fCTM = &this->localToDevice();
draw.fRC = &rc;
draw.drawDevicePoints(mode, count, pts, paint, this);
return;
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, SkToS32(count), pts, nullptr,
nullptr);
GrPrimitiveType primitiveType = point_mode_to_primitive_type(mode);
fSurfaceDrawContext->drawVertices(this->clip(), std::move(grPaint), this->localToDevice(),
std::move(vertices), &primitiveType);
}
///////////////////////////////////////////////////////////////////////////////
void Device::drawRect(const SkRect& rect, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawRect", fContext.get());
GrStyle style(paint);
// A couple reasons we might need to call drawPath.
if (paint.getMaskFilter() || paint.getPathEffect()) {
GrStyledShape shape(rect, style);
GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(),
this->clip(), paint, this->localToDevice(), shape);
return;
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawRect(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), rect,
&style);
}
void Device::drawEdgeAAQuad(const SkRect& rect,
const SkPoint clip[4],
SkCanvas::QuadAAFlags aaFlags,
const SkColor4f& color,
SkBlendMode mode) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawEdgeAAQuad", fContext.get());
SkPMColor4f dstColor = SkColor4fPrepForDst(color, fSurfaceDrawContext->colorInfo()).premul();
GrPaint grPaint;
grPaint.setColor4f(dstColor);
if (mode != SkBlendMode::kSrcOver) {
grPaint.setXPFactory(GrXPFactory::FromBlendMode(mode));
}
if (clip) {
// Use fillQuadWithEdgeAA
fSurfaceDrawContext->fillQuadWithEdgeAA(this->clip(),
std::move(grPaint),
SkToGrQuadAAFlags(aaFlags),
this->localToDevice(),
clip,
nullptr);
} else {
// Use fillRectWithEdgeAA to preserve mathematical properties of dst being rectangular
fSurfaceDrawContext->fillRectWithEdgeAA(this->clip(),
std::move(grPaint),
SkToGrQuadAAFlags(aaFlags),
this->localToDevice(),
rect);
}
}
///////////////////////////////////////////////////////////////////////////////
void Device::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawRRect", fContext.get());
auto mf = paint.getMaskFilter();
if (mf) {
if (GrFragmentProcessors::IsSupported(mf)) {
mf = nullptr; // already handled in SkPaintToGrPaint
}
}
GrStyle style(paint);
if (mf || style.pathEffect()) {
// A path effect will presumably transform this rrect into something else.
GrStyledShape shape(rrect, style);
GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(),
this->clip(), paint, this->localToDevice(), shape);
return;
}
SkASSERT(!style.pathEffect());
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawRRect(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint), this->localToDevice(),
rrect, style);
}
void Device::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawDRRect", fContext.get());
if (outer.isEmpty()) {
return;
}
if (inner.isEmpty()) {
return this->drawRRect(outer, paint);
}
SkStrokeRec stroke(paint);
if (stroke.isFillStyle() && !paint.getMaskFilter() && !paint.getPathEffect()) {
// For axis-aligned filled DRRects, just draw a regular rrect with inner clipped out using a
// coverage FP instead of using path rendering.
if (auto fp = make_inverse_rrect_fp(this->localToDevice(), inner,
fSurfaceDrawContext->chooseAA(paint),
*fSurfaceDrawContext->caps()->shaderCaps())) {
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
SkASSERT(!grPaint.hasCoverageFragmentProcessor());
grPaint.setCoverageFragmentProcessor(std::move(fp));
fSurfaceDrawContext->drawRRect(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint),
this->localToDevice(), outer, GrStyle());
return;
}
}
SkPath path;
path.setIsVolatile(true);
path.addRRect(outer);
path.addRRect(inner);
path.setFillType(SkPathFillType::kEvenOdd);
// TODO: We are losing the possible mutability of the path here but this should probably be
// fixed by upgrading GrStyledShape to handle DRRects.
GrStyledShape shape(path, paint);
GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(),
paint, this->localToDevice(), shape);
}
/////////////////////////////////////////////////////////////////////////////
void Device::drawRegion(const SkRegion& region, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
if (paint.getMaskFilter()) {
SkPath path;
region.getBoundaryPath(&path);
path.setIsVolatile(true);
return this->drawPath(path, paint, true);
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawRegion(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint), this->localToDevice(),
region, GrStyle(paint));
}
void Device::drawOval(const SkRect& oval, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawOval", fContext.get());
if (paint.getMaskFilter()) {
// The RRect path can handle special case blurring
SkRRect rr = SkRRect::MakeOval(oval);
return this->drawRRect(rr, paint);
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawOval(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), oval,
GrStyle(paint));
}
void Device::drawArc(const SkArc& arc, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawArc", fContext.get());
if (paint.getMaskFilter()) {
this->SkDevice::drawArc(arc, paint);
return;
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawArc(this->clip(),
std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint),
this->localToDevice(),
arc,
GrStyle(paint));
}
///////////////////////////////////////////////////////////////////////////////
void Device::drawPath(const SkPath& origSrcPath, const SkPaint& paint, bool pathIsMutable) {
#if defined(GR_TEST_UTILS)
if (fContext->priv().options().fAllPathsVolatile && !origSrcPath.isVolatile()) {
this->drawPath(SkPath(origSrcPath).setIsVolatile(true), paint, true);
return;
}
#endif
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawPath", fContext.get());
if (!paint.getMaskFilter()) {
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawPath(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint), this->localToDevice(),
origSrcPath, GrStyle(paint));
return;
}
// TODO: losing possible mutability of 'origSrcPath' here
GrStyledShape shape(origSrcPath, paint);
GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(),
paint, this->localToDevice(), shape);
}
sk_sp<skif::Backend> Device::createImageFilteringBackend(const SkSurfaceProps& surfaceProps,
SkColorType colorType) const {
return skif::MakeGaneshBackend(
fContext, fSurfaceDrawContext->origin(), surfaceProps, colorType);
}
sk_sp<SkSpecialImage> Device::makeSpecial(const SkBitmap& bitmap) {
ASSERT_SINGLE_OWNER
// TODO: this makes a tight copy of 'bitmap' but it doesn't have to be (given SkSpecialImage's
// semantics). Since this is cached we would have to bake the fit into the cache key though.
auto view = std::get<0>(
GrMakeCachedBitmapProxyView(fContext.get(), bitmap, /*label=*/"Device_MakeSpecial"));
if (!view) {
return nullptr;
}
const SkIRect rect = SkIRect::MakeSize(view.proxy()->dimensions());
// GrMakeCachedBitmapProxyView creates a tight copy of 'bitmap' so we don't have to subset
// the special image
return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
rect,
bitmap.getGenerationID(),
std::move(view),
{SkColorTypeToGrColorType(bitmap.colorType()),
kPremul_SkAlphaType,
bitmap.refColorSpace()},
this->surfaceProps());
}
sk_sp<SkSpecialImage> Device::makeSpecial(const SkImage* image) {
ASSERT_SINGLE_OWNER
SkPixmap pm;
if (image->isTextureBacked()) {
auto [view, ct] =
skgpu::ganesh::AsView(this->recordingContext(), image, skgpu::Mipmapped::kNo);
SkASSERT(view);
return SkSpecialImages::MakeDeferredFromGpu(
fContext.get(),
SkIRect::MakeWH(image->width(), image->height()),
image->uniqueID(),
std::move(view),
{ct, kPremul_SkAlphaType, image->refColorSpace()},
this->surfaceProps());
} else if (image->peekPixels(&pm)) {
SkBitmap bm;
bm.installPixels(pm);
return this->makeSpecial(bm);
} else {
return nullptr;
}
}
sk_sp<SkSpecialImage> Device::snapSpecial(const SkIRect& subset, bool forceCopy) {
ASSERT_SINGLE_OWNER
auto sdc = fSurfaceDrawContext.get();
// If we are wrapping a vulkan secondary command buffer, then we can't snap off a special image
// since it would require us to make a copy of the underlying VkImage which we don't have access
// to. Additionaly we can't stop and start the render pass that is used with the secondary
// command buffer.
if (sdc->wrapsVkSecondaryCB()) {
return nullptr;
}
SkASSERT(sdc->asSurfaceProxy());
SkIRect finalSubset = subset;
GrSurfaceProxyView view = sdc->readSurfaceView();
if (forceCopy || !view.asTextureProxy()) {
// When the device doesn't have a texture, or a copy is requested, we create a temporary
// texture that matches the device contents
view = GrSurfaceProxyView::Copy(fContext.get(),
std::move(view),
skgpu::Mipmapped::kNo, // Don't auto generate mips
subset,
SkBackingFit::kApprox,
skgpu::Budgeted::kYes,
/*label=*/"Device_SnapSpecial"); // Always budgeted
if (!view) {
return nullptr;
}
// Since this copied only the requested subset, the special image wrapping the proxy no
// longer needs the original subset.
finalSubset = SkIRect::MakeSize(view.dimensions());
}
return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
finalSubset,
kNeedNewImageUniqueID_SpecialImage,
std::move(view),
GrColorInfo(this->imageInfo().colorInfo()),
this->surfaceProps());
}
sk_sp<SkSpecialImage> Device::snapSpecialScaled(const SkIRect& subset, const SkISize& dstDims) {
ASSERT_SINGLE_OWNER
auto sdc = fSurfaceDrawContext.get();
// If we are wrapping a vulkan secondary command buffer, then we can't snap off a special image
// since it would require us to make a copy of the underlying VkImage which we don't have access
// to. Additionaly we can't stop and start the render pass that is used with the secondary
// command buffer.
if (sdc->wrapsVkSecondaryCB()) {
return nullptr;
}
SkASSERT(sdc->asSurfaceProxy());
auto scaledContext = sdc->rescale(sdc->imageInfo().makeDimensions(dstDims),
sdc->origin(),
subset,
RescaleGamma::kSrc,
RescaleMode::kLinear);
if (!scaledContext) {
return nullptr;
}
return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
SkIRect::MakeSize(dstDims),
kNeedNewImageUniqueID_SpecialImage,
scaledContext->readSurfaceView(),
GrColorInfo(this->imageInfo().colorInfo()),
this->surfaceProps());
}
void Device::drawDevice(SkDevice* device,
const SkSamplingOptions& sampling,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
// clear of the source device must occur before CHECK_SHOULD_DRAW
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawDevice", fContext.get());
this->SkDevice::drawDevice(device, sampling, paint);
}
void Device::drawImageRect(const SkImage* image,
const SkRect* src,
const SkRect& dst,
const SkSamplingOptions& sampling,
const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
ASSERT_SINGLE_OWNER
GrAA aa = fSurfaceDrawContext->chooseAA(paint);
SkCanvas::QuadAAFlags aaFlags = (aa == GrAA::kYes) ? SkCanvas::kAll_QuadAAFlags
: SkCanvas::kNone_QuadAAFlags;
this->drawImageQuadDirect(image,
src ? *src
: SkRect::MakeIWH(image->width(), image->height()),
dst,
/* dstClip= */ nullptr,
aaFlags,
/* preViewMatrix= */ nullptr,
sampling,
paint,
constraint);
}
bool Device::drawAsTiledImageRect(SkCanvas* canvas,
const SkImage* image,
const SkRect* src,
const SkRect& dst,
const SkSamplingOptions& sampling,
const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
GrRecordingContext* rCtx = canvas->recordingContext();
if (!rCtx) {
return false;
}
ASSERT_SINGLE_OWNER
GrAA aa = fSurfaceDrawContext->chooseAA(paint);
SkCanvas::QuadAAFlags aaFlags = (aa == GrAA::kYes) ? SkCanvas::kAll_QuadAAFlags
: SkCanvas::kNone_QuadAAFlags;
// NOTE: if the context is not a direct context, it doesn't have access to the resource
// cache, and theoretically, the resource cache's limits could be being changed on
// another thread, so even having access to just the limit wouldn't be a reliable
// test during recording here.
size_t cacheSize = 0;
if (auto dCtx = GrAsDirectContext(rCtx)) {
cacheSize = dCtx->getResourceCacheLimit();
}
size_t maxTextureSize = rCtx->maxTextureSize();
#if defined(GR_TEST_UTILS)
if (gOverrideMaxTextureSizeGanesh) {
maxTextureSize = gOverrideMaxTextureSizeGanesh;
}
gNumTilesDrawnGanesh.store(0, std::memory_order_relaxed);
#endif
[[maybe_unused]] auto [wasTiled, numTiles] = TiledTextureUtils::DrawAsTiledImageRect(
canvas,
image,
src ? *src : SkRect::MakeIWH(image->width(), image->height()),
dst,
aaFlags,
sampling,
&paint,
constraint,
cacheSize,
maxTextureSize);
#if defined(GR_TEST_UTILS)
gNumTilesDrawnGanesh.store(numTiles, std::memory_order_relaxed);
#endif
return wasTiled;
}
void Device::drawViewLattice(GrSurfaceProxyView view,
const GrColorInfo& info,
std::unique_ptr<SkLatticeIter> iter,
const SkRect& dst,
SkFilterMode filter,
const SkPaint& origPaint) {
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawViewLattice", fContext.get());
SkASSERT(view);
SkTCopyOnFirstWrite<SkPaint> paint(&origPaint);
if (!info.isAlphaOnly() && (paint->getColor() & 0x00FFFFFF) != 0x00FFFFFF) {
paint.writable()->setColor(SkColorSetARGB(origPaint.getAlpha(), 0xFF, 0xFF, 0xFF));
}
GrPaint grPaint;
// Passing null as shaderFP indicates that the GP will provide the shader.
if (!SkPaintToGrPaintReplaceShader(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
*paint,
this->localToDevice(),
/*shaderFP=*/nullptr,
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
if (info.isAlphaOnly()) {
// If we were doing this with an FP graph we'd use a kDstIn blend between the texture
// and the paint color.
view.concatSwizzle(skgpu::Swizzle("aaaa"));
}
auto csxf = GrColorSpaceXform::Make(info, fSurfaceDrawContext->colorInfo());
fSurfaceDrawContext->drawImageLattice(this->clip(),
std::move(grPaint),
this->localToDevice(),
std::move(view),
info.alphaType(),
std::move(csxf),
filter,
std::move(iter),
dst);
}
void Device::drawImageLattice(const SkImage* image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
auto iter = std::make_unique<SkLatticeIter>(lattice, dst);
auto [view, ct] = skgpu::ganesh::AsView(this->recordingContext(), image, skgpu::Mipmapped::kNo);
if (view) {
GrColorInfo colorInfo(ct, image->alphaType(), image->refColorSpace());
this->drawViewLattice(
std::move(view), std::move(colorInfo), std::move(iter), dst, filter, paint);
}
}
void Device::drawVertices(const SkVertices* vertices,
sk_sp<SkBlender> blender,
const SkPaint& paint,
bool skipColorXform) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawVertices", fContext.get());
SkASSERT(vertices);
#ifdef SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER
if (!paint.getShader()) {
blender = SkBlender::Mode(SkBlendMode::kDst);
}
#endif
SkVerticesPriv info(vertices->priv());
GrPaint grPaint;
if (!init_vertices_paint(fContext.get(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
blender.get(),
info.hasColors(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
fSurfaceDrawContext->drawVertices(this->clip(),
std::move(grPaint),
this->localToDevice(),
sk_ref_sp(const_cast<SkVertices*>(vertices)),
nullptr,
skipColorXform);
}
void Device::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawMesh", fContext.get());
if (!mesh.isValid()) {
return;
}
GrPaint grPaint;
if (!init_vertices_paint(fContext.get(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
blender.get(),
SkMeshSpecificationPriv::HasColors(*mesh.spec()),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
TArray<std::unique_ptr<GrFragmentProcessor>> meshChildFPs;
if (!init_mesh_child_effects(fContext.get(),
fSurfaceDrawContext->colorInfo(),
fSurfaceDrawContext->surfaceProps(),
mesh,
&meshChildFPs)) {
return;
}
fSurfaceDrawContext->drawMesh(this->clip(), std::move(grPaint), this->localToDevice(), mesh,
std::move(meshChildFPs));
}
///////////////////////////////////////////////////////////////////////////////
#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
void Device::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
#if defined(GR_TEST_UTILS)
if (fContext->priv().options().fAllPathsVolatile && !path.isVolatile()) {
this->drawShadow(SkPath(path).setIsVolatile(true), rec);
return;
}
#endif
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawShadow", fContext.get());
if (!fSurfaceDrawContext->drawFastShadow(this->clip(), this->localToDevice(), path, rec)) {
// failed to find an accelerated case
this->SkDevice::drawShadow(path, rec);
}
}
#endif // SK_ENABLE_OPTIMIZE_SIZE
///////////////////////////////////////////////////////////////////////////////
void Device::drawAtlas(const SkRSXform xform[],
const SkRect texRect[],
const SkColor colors[],
int count,
sk_sp<SkBlender> blender,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawAtlas", fContext.get());
GrPaint grPaint;
if (colors) {
if (!SkPaintToGrPaintWithBlend(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
blender.get(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
} else {
if (!SkPaintToGrPaint(this->recordingContext(),
fSurfaceDrawContext->colorInfo(),
paint,
this->localToDevice(),
fSurfaceDrawContext->surfaceProps(),
&grPaint)) {
return;
}
}
fSurfaceDrawContext->drawAtlas(this->clip(), std::move(grPaint), this->localToDevice(), count,
xform, texRect, colors);
}
///////////////////////////////////////////////////////////////////////////////
void Device::onDrawGlyphRunList(SkCanvas* canvas,
const sktext::GlyphRunList& glyphRunList,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawGlyphRunList", fContext.get());
SkASSERT(!glyphRunList.hasRSXForm());
if (glyphRunList.blob() == nullptr) {
// If the glyphRunList does not have an associated text blob, then it was created by one of
// the direct draw APIs (drawGlyphs, etc.). Use a Slug to draw the glyphs.
auto slug = this->convertGlyphRunListToSlug(glyphRunList, paint);
if (slug != nullptr) {
this->drawSlug(canvas, slug.get(), paint);
}
} else {
fSurfaceDrawContext->drawGlyphRunList(canvas,
this->clip(),
this->localToDevice(),
glyphRunList,
this->strikeDeviceInfo(),
paint);
}
}
///////////////////////////////////////////////////////////////////////////////
void Device::drawDrawable(SkCanvas* canvas, SkDrawable* drawable, const SkMatrix* matrix) {
ASSERT_SINGLE_OWNER
GrBackendApi api = this->recordingContext()->backend();
if (GrBackendApi::kVulkan == api) {
const SkMatrix& ctm = this->localToDevice();
const SkMatrix& combinedMatrix = matrix ? SkMatrix::Concat(ctm, *matrix) : ctm;
std::unique_ptr<SkDrawable::GpuDrawHandler> gpuDraw =
drawable->snapGpuDrawHandler(api, combinedMatrix, this->devClipBounds(),
this->imageInfo());
if (gpuDraw) {
fSurfaceDrawContext->drawDrawable(
std::move(gpuDraw), combinedMatrix.mapRect(drawable->getBounds()));
return;
}
}
this->SkDevice::drawDrawable(canvas, drawable, matrix);
}
///////////////////////////////////////////////////////////////////////////////
GrSurfaceProxyView Device::readSurfaceView() {
return this->surfaceFillContext()->readSurfaceView();
}
GrRenderTargetProxy* Device::targetProxy() {
return this->readSurfaceView().asRenderTargetProxy();
}
bool Device::wait(int numSemaphores,
const GrBackendSemaphore* waitSemaphores,
bool deleteSemaphoresAfterWait) {
ASSERT_SINGLE_OWNER
return fSurfaceDrawContext->waitOnSemaphores(numSemaphores, waitSemaphores,
deleteSemaphoresAfterWait);
}
void Device::discard() {
fSurfaceDrawContext->discard();
}
void Device::resolveMSAA() {
fSurfaceDrawContext->resolveMSAA();
}
bool Device::replaceBackingProxy(SkSurface::ContentChangeMode mode,
sk_sp<GrRenderTargetProxy> newRTP,
GrColorType grColorType,
sk_sp<SkColorSpace> colorSpace,
GrSurfaceOrigin origin,
const SkSurfaceProps& props) {
auto sdc = SurfaceDrawContext::Make(fContext.get(), grColorType, std::move(newRTP),
std::move(colorSpace), origin, props);
if (!sdc) {
return false;
}
SkASSERT(sdc->dimensions() == fSurfaceDrawContext->dimensions());
SkASSERT(sdc->numSamples() == fSurfaceDrawContext->numSamples());
SkASSERT(sdc->asSurfaceProxy()->priv().isExact());
if (mode == SkSurface::kRetain_ContentChangeMode) {
if (fContext->abandoned()) {
return false;
}
SkASSERT(fSurfaceDrawContext->asTextureProxy());
SkAssertResult(sdc->blitTexture(fSurfaceDrawContext->readSurfaceView(),
SkIRect::MakeWH(this->width(), this->height()),
SkIPoint::Make(0, 0)));
}
fSurfaceDrawContext = std::move(sdc);
return true;
}
bool Device::replaceBackingProxy(SkSurface::ContentChangeMode mode) {
ASSERT_SINGLE_OWNER
const SkImageInfo& ii = this->imageInfo();
GrRenderTargetProxy* oldRTP = this->targetProxy();
GrSurfaceProxyView oldView = this->readSurfaceView();
auto grColorType = SkColorTypeToGrColorType(ii.colorType());
auto format = fContext->priv().caps()->getDefaultBackendFormat(grColorType, GrRenderable::kYes);
if (!format.isValid()) {
return false;
}
GrProxyProvider* proxyProvider = fContext->priv().proxyProvider();
// This entry point is used by SkSurface_Ganesh::onCopyOnWrite so it must create a
// kExact-backed render target proxy
sk_sp<GrTextureProxy> proxy =
proxyProvider->createProxy(format,
ii.dimensions(),
GrRenderable::kYes,
oldRTP->numSamples(),
oldView.mipmapped(),
SkBackingFit::kExact,
oldRTP->isBudgeted(),
GrProtected::kNo,
/*label=*/"BaseDevice_ReplaceBackingProxy");
if (!proxy) {
return false;
}
return this->replaceBackingProxy(mode, sk_ref_sp(proxy->asRenderTargetProxy()),
grColorType, ii.refColorSpace(), oldView.origin(),
this->surfaceProps());
}
void Device::asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
RescaleMode rescaleMode,
ReadPixelsCallback callback,
ReadPixelsContext context) {
auto* sdc = fSurfaceDrawContext.get();
// Context TODO: Elevate direct context requirement to public API.
auto dContext = sdc->recordingContext()->asDirectContext();
if (!dContext) {
return;
}
sdc->asyncRescaleAndReadPixels(dContext, info, srcRect, rescaleGamma, rescaleMode, callback,
context);
}
void Device::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
bool readAlpha,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
SkISize dstSize,
RescaleGamma rescaleGamma,
RescaleMode rescaleMode,
ReadPixelsCallback callback,
ReadPixelsContext context) {
auto* sdc = fSurfaceDrawContext.get();
// Context TODO: Elevate direct context requirement to public API.
auto dContext = sdc->recordingContext()->asDirectContext();
if (!dContext) {
return;
}
sdc->asyncRescaleAndReadPixelsYUV420(dContext,
yuvColorSpace,
readAlpha,
std::move(dstColorSpace),
srcRect,
dstSize,
rescaleGamma,
rescaleMode,
callback,
context);
}
///////////////////////////////////////////////////////////////////////////////
sk_sp<SkDevice> Device::createDevice(const CreateInfo& cinfo, const SkPaint*) {
ASSERT_SINGLE_OWNER
SkSurfaceProps props =
this->surfaceProps().cloneWithPixelGeometry(cinfo.fPixelGeometry);
SkASSERT(cinfo.fInfo.colorType() != kRGBA_1010102_SkColorType);
auto sdc = SurfaceDrawContext::MakeWithFallback(
fContext.get(),
SkColorTypeToGrColorType(cinfo.fInfo.colorType()),
cinfo.fInfo.refColorSpace(),
SkBackingFit::kApprox,
cinfo.fInfo.dimensions(),
props,
fSurfaceDrawContext->numSamples(),
skgpu::Mipmapped::kNo,
fSurfaceDrawContext->asSurfaceProxy()->isProtected(),
fSurfaceDrawContext->origin(),
skgpu::Budgeted::kYes);
if (!sdc) {
return nullptr;
}
// Skia's convention is to only clear a device if it is non-opaque.
InitContents init = cinfo.fInfo.isOpaque() ? InitContents::kUninit : InitContents::kClear;
return Device::Make(std::move(sdc), cinfo.fInfo.alphaType(), init);
}
sk_sp<SkSurface> Device::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
ASSERT_SINGLE_OWNER
// TODO: Change the signature of newSurface to take a budgeted parameter.
static const skgpu::Budgeted kBudgeted = skgpu::Budgeted::kNo;
bool isProtected = this->targetProxy()->isProtected() == GrProtected::kYes;
return SkSurfaces::RenderTarget(fContext.get(),
kBudgeted,
info,
fSurfaceDrawContext->numSamples(),
fSurfaceDrawContext->origin(),
&props,
/* shouldCreateWithMips= */ false,
isProtected);
}
////////////////////////////////////////////////////////////////////////////////////
bool Device::android_utils_clipWithStencil() {
SkRegion clipRegion;
this->android_utils_clipAsRgn(&clipRegion);
if (clipRegion.isEmpty()) {
return false;
}
auto sdc = fSurfaceDrawContext.get();
SkASSERT(sdc);
GrPaint grPaint;
grPaint.setXPFactory(GrDisableColorXPFactory::Get());
static constexpr GrUserStencilSettings kDrawToStencil(
GrUserStencilSettings::StaticInit<
0x1,
GrUserStencilTest::kAlways,
0x1,
GrUserStencilOp::kReplace,
GrUserStencilOp::kReplace,
0x1>()
);
// Regions don't actually need AA, but in DMSAA mode everything is antialiased.
GrAA aa = GrAA(fSurfaceDrawContext->alwaysAntialias());
sdc->drawRegion(nullptr, std::move(grPaint), aa, SkMatrix::I(), clipRegion,
GrStyle::SimpleFill(), &kDrawToStencil);
return true;
}
SkStrikeDeviceInfo Device::strikeDeviceInfo() const {
return {this->surfaceProps(), this->scalerContextFlags(), &fSDFTControl};
}
sk_sp<sktext::gpu::Slug> Device::convertGlyphRunListToSlug(const sktext::GlyphRunList& glyphRunList,
const SkPaint& paint) {
return sktext::gpu::SlugImpl::Make(this->localToDevice(),
glyphRunList,
paint,
this->strikeDeviceInfo(),
SkStrikeCache::GlobalStrikeCache());
}
void Device::drawSlug(SkCanvas* canvas, const sktext::gpu::Slug* slug, const SkPaint& paint) {
SkASSERT(canvas);
SkASSERT(slug);
const sktext::gpu::SlugImpl* slugImpl = static_cast<const sktext::gpu::SlugImpl*>(slug);
#if defined(SK_DEBUG)
if (!fContext->priv().options().fSupportBilerpFromGlyphAtlas) {
// We can draw a slug if the atlas has padding or if the creation matrix and the
// drawing matrix are the same. If they are the same, then the Slug will use the direct
// drawing code and not use bi-lerp.
SkMatrix slugMatrix = slugImpl->initialPositionMatrix();
SkMatrix positionMatrix = this->localToDevice();
positionMatrix.preTranslate(slugImpl->origin().x(), slugImpl->origin().y());
SkASSERT(slugMatrix == positionMatrix);
}
#endif
auto atlasDelegate = [&](const sktext::gpu::AtlasSubRun* subRun,
SkPoint drawOrigin,
const SkPaint& paint,
sk_sp<SkRefCnt> subRunStorage,
sktext::gpu::RendererData) {
auto[drawingClip, op] = subRun->makeAtlasTextOp(
this->clip(), this->localToDevice(), drawOrigin, paint,
std::move(subRunStorage), fSurfaceDrawContext.get());
if (op != nullptr) {
fSurfaceDrawContext->addDrawOp(drawingClip, std::move(op));
}
};
slugImpl->subRuns()->draw(canvas, slugImpl->origin(), paint, slugImpl, atlasDelegate);
}
} // namespace skgpu::ganesh