bool SkAndroidFrameworkUtils::clipWithStencil(SkCanvas* canvas) { SkRegion clipRegion; canvas->temporary_internal_getRgnClip(&clipRegion); if (clipRegion.isEmpty()) { return false; } SkBaseDevice* device = canvas->getDevice(); if (!device) { return false; } GrRenderTargetContext* rtc = device->accessRenderTargetContext(); if (!rtc) { return false; } GrPaint grPaint; grPaint.setXPFactory(GrDisableColorXPFactory::Get()); GrNoClip noClip; static constexpr GrUserStencilSettings kDrawToStencil( GrUserStencilSettings::StaticInit< 0x1, GrUserStencilTest::kAlways, 0x1, GrUserStencilOp::kReplace, GrUserStencilOp::kReplace, 0x1>() ); rtc->drawRegion(noClip, std::move(grPaint), GrAA::kNo, SkMatrix::I(), clipRegion, GrStyle::SimpleFill(), &kDrawToStencil); return true; }
void onDraw(SkCanvas* canvas) override { GrRenderTargetContext* renderTargetContext = canvas->internal_private_accessTopLayerRenderTargetContext(); if (!renderTargetContext) { skiagm::GM::DrawGpuOnlyMessage(canvas); return; } GrContext* context = canvas->getGrContext(); if (!context) { return; } GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); sk_sp<GrTextureProxy> proxy[3]; for (int i = 0; i < 3; ++i) { int index = (0 == i) ? 0 : 1; GrSurfaceDesc desc; desc.fWidth = fBmp[index].width(); desc.fHeight = fBmp[index].height(); desc.fConfig = SkImageInfo2GrPixelConfig(fBmp[index].info(), *context->caps()); SkASSERT(kUnknown_GrPixelConfig != desc.fConfig); proxy[i] = proxyProvider->createTextureProxy( desc, SkBudgeted::kYes, fBmp[index].getPixels(), fBmp[index].rowBytes()); if (!proxy[i]) { return; } } constexpr SkScalar kDrawPad = 10.f; constexpr SkScalar kTestPad = 10.f; constexpr SkScalar kColorSpaceOffset = 36.f; SkISize sizes[3] = {{YSIZE, YSIZE}, {USIZE, USIZE}, {VSIZE, VSIZE}}; for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()), SkIntToScalar(fBmp[0].height())); renderRect.outset(kDrawPad, kDrawPad); SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset; SkScalar x = kDrawPad + kTestPad; GrPaint grPaint; grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); auto fp = GrYUVtoRGBEffect::Make(proxy[0], proxy[1], proxy[2], sizes, static_cast<SkYUVColorSpace>(space), true); if (fp) { SkMatrix viewMatrix; viewMatrix.setTranslate(x, y); grPaint.addColorFragmentProcessor(std::move(fp)); std::unique_ptr<GrDrawOp> op(GrRectOpFactory::MakeNonAAFill( std::move(grPaint), viewMatrix, renderRect, GrAAType::kNone)); renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); } } }
bool SkSurface_Gpu::onDraw(const SkDeferredDisplayList* ddl) { if (!ddl || !this->isCompatible(ddl->characterization())) { return false; } GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); GrContext* ctx = fDevice->context(); ctx->contextPriv().copyOpListsFromDDL(ddl, rtc->asRenderTargetProxy()); return true; }
static GrRenderTarget* prepare_rt_for_external_access(SkSurface_Gpu* surface, SkSurface::BackendHandleAccess access) { switch (access) { case SkSurface::kFlushRead_BackendHandleAccess: break; case SkSurface::kFlushWrite_BackendHandleAccess: case SkSurface::kDiscardWrite_BackendHandleAccess: // for now we don't special-case on Discard, but we may in the future. surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode); break; } // Grab the render target *after* firing notifications, as it may get switched if CoW kicks in. surface->getDevice()->flush(); GrRenderTargetContext* rtc = surface->getDevice()->accessRenderTargetContext(); return rtc->accessRenderTarget(); }
bool SkSurface_Gpu::onDraw(const SkDeferredDisplayList* ddl) { if (!ddl || !this->isCompatible(ddl->characterization())) { return false; } #ifdef SK_RASTER_RECORDER_IMPLEMENTATION // Ultimately need to pass opLists from the DeferredDisplayList on to the // SkGpuDevice's renderTargetContext. return ddl->draw(this); #else GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); GrContext* ctx = fDevice->context(); ctx->contextPriv().copyOpListsFromDDL(ddl, rtc->asRenderTargetProxy()); return true; #endif }
// Create a new render target and, if necessary, copy the contents of the old // render target into it. Note that this flushes the SkGpuDevice but // doesn't force an OpenGL flush. void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) { GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); // are we sharing our backing proxy with the image? Note this call should never create a new // image because onCopyOnWrite is only called when there is a cached image. sk_sp<SkImage> image(this->refCachedImage()); SkASSERT(image); GrSurfaceProxy* imageProxy = ((SkImage_Base*) image.get())->peekProxy(); SkASSERT(imageProxy); if (rtc->asSurfaceProxy()->underlyingUniqueID() == imageProxy->underlyingUniqueID()) { fDevice->replaceRenderTargetContext(SkSurface::kRetain_ContentChangeMode == mode); } else if (kDiscard_ContentChangeMode == mode) { this->SkSurface_Gpu::onDiscard(); } }
void onDraw(int loops, SkCanvas* canvas) override { static const SkRect kPartialClip = SkRect::MakeLTRB(50, 50, 400, 400); static const SkRRect kComplexClip = SkRRect::MakeRectXY(kPartialClip, 15, 15); // Small to limit fill cost, but intersects the clips to confound batching static const SkRect kInterruptRect = SkRect::MakeXYWH(200, 200, 3, 3); // For the draw that sits between consecutive clears, use a shader that is simple but // requires local coordinates so that Ganesh does not convert it into a solid color rect, // which could then turn into a scissored-clear behind the scenes. SkPaint interruptPaint; interruptPaint.setShader(make_shader()); GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext(); if (rtc) { // Tricks the GrRenderTargetOpList into thinking it cannot reset its draw op list on // a fullscreen clear. If we don't do this, fullscreen clear ops would be created and // constantly discard the previous iteration's op so execution would only invoke one // actual clear on the GPU (not what we want to measure). rtc->setNeedsStencil(); } for (int i = 0; i < loops; i++) { canvas->save(); switch(fType) { case kPartial_ClearType: canvas->clipRect(kPartialClip); break; case kComplex_ClearType: canvas->clipRRect(kComplexClip); break; case kFull_ClearType: // Don't add any extra clipping, since it defaults to the entire "device" break; } // The clear we care about measuring canvas->clear(SK_ColorBLUE); canvas->restore(); // Perform as minimal a draw as possible that intersects with the clear region in // order to prevent the clear ops from being batched together. canvas->drawRect(kInterruptRect, interruptPaint); } }
sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot() { GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); if (!rtc) { return nullptr; } GrContext* ctx = fDevice->context(); if (!rtc->asSurfaceProxy()) { return nullptr; } SkBudgeted budgeted = rtc->asSurfaceProxy()->isBudgeted(); sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef(); // If the original render target is a buffer originally created by the client, then we don't // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid // copy-on-write. if (!srcProxy || rtc->priv().refsWrappedObjects()) { SkASSERT(rtc->origin() == rtc->asSurfaceProxy()->origin()); srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(), budgeted); } const SkImageInfo info = fDevice->imageInfo(); sk_sp<SkImage> image; if (srcProxy) { // The renderTargetContext coming out of SkGpuDevice should always be exact and the // above copy creates a kExact surfaceContext. SkASSERT(srcProxy->priv().isExact()); image = sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID, info.alphaType(), std::move(srcProxy), info.refColorSpace(), budgeted); } return image; }
bool SkSurface_Gpu::isCompatible(const SkSurfaceCharacterization& data) const { GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); GrContext* ctx = fDevice->context(); if (!data.isValid()) { return false; } // As long as the current state if the context allows for greater or equal resources, // we allow the DDL to be replayed. // DDL TODO: should we just remove the resource check and ignore the cache limits on playback? int maxResourceCount; size_t maxResourceBytes; ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes); if (data.isTextureable()) { if (!rtc->asTextureProxy()) { // If the characterization was textureable we require the replay dest to also be // textureable. If the characterized surface wasn't textureable we allow the replay // dest to be textureable. return false; } if (data.isMipMapped() && GrMipMapped::kNo == rtc->asTextureProxy()->mipMapped()) { // Fail if the DDL's surface was mipmapped but the replay surface is not. // Allow drawing to proceed if the DDL was not mipmapped but the replay surface is. return false; } } return data.contextInfo() && data.contextInfo()->matches(ctx) && data.cacheMaxResourceBytes() <= maxResourceBytes && data.origin() == rtc->origin() && data.width() == rtc->width() && data.height() == rtc->height() && data.config() == rtc->colorSpaceInfo().config() && data.fsaaType() == rtc->fsaaType() && data.stencilCount() == rtc->numStencilSamples() && SkColorSpace::Equals(data.colorSpace(), rtc->colorSpaceInfo().colorSpace()) && data.surfaceProps() == rtc->surfaceProps(); }
bool SkSurface_Gpu::onCharacterize(SkSurfaceCharacterization* data) const { GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); GrContext* ctx = fDevice->context(); int maxResourceCount; size_t maxResourceBytes; ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes); bool mipmapped = rtc->asTextureProxy() ? GrMipMapped::kYes == rtc->asTextureProxy()->mipMapped() : false; data->set(ctx->threadSafeProxy(), maxResourceBytes, rtc->origin(), rtc->width(), rtc->height(), rtc->colorSpaceInfo().config(), rtc->fsaaType(), rtc->numStencilSamples(), SkSurfaceCharacterization::Textureable(SkToBool(rtc->asTextureProxy())), SkSurfaceCharacterization::MipMapped(mipmapped), rtc->colorSpaceInfo().refColorSpace(), this->props()); return true; }