1 | /* |
2 | * Copyright 2015 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "src/gpu/GrRenderTargetContext.h" |
9 | |
10 | #include "include/core/SkDrawable.h" |
11 | #include "include/core/SkVertices.h" |
12 | #include "include/gpu/GrBackendSemaphore.h" |
13 | #include "include/gpu/GrDirectContext.h" |
14 | #include "include/gpu/GrRecordingContext.h" |
15 | #include "include/private/GrImageContext.h" |
16 | #include "include/private/SkShadowFlags.h" |
17 | #include "include/utils/SkShadowUtils.h" |
18 | #include "src/core/SkAutoPixmapStorage.h" |
19 | #include "src/core/SkConvertPixels.h" |
20 | #include "src/core/SkDrawShadowInfo.h" |
21 | #include "src/core/SkGlyphRunPainter.h" |
22 | #include "src/core/SkLatticeIter.h" |
23 | #include "src/core/SkMatrixPriv.h" |
24 | #include "src/core/SkMatrixProvider.h" |
25 | #include "src/core/SkRRectPriv.h" |
26 | #include "src/core/SkSurfacePriv.h" |
27 | #include "src/gpu/GrAppliedClip.h" |
28 | #include "src/gpu/GrAuditTrail.h" |
29 | #include "src/gpu/GrBlurUtils.h" |
30 | #include "src/gpu/GrCaps.h" |
31 | #include "src/gpu/GrClip.h" |
32 | #include "src/gpu/GrColor.h" |
33 | #include "src/gpu/GrContextPriv.h" |
34 | #include "src/gpu/GrDataUtils.h" |
35 | #include "src/gpu/GrDrawingManager.h" |
36 | #include "src/gpu/GrGpuResourcePriv.h" |
37 | #include "src/gpu/GrImageContextPriv.h" |
38 | #include "src/gpu/GrImageInfo.h" |
39 | #include "src/gpu/GrMemoryPool.h" |
40 | #include "src/gpu/GrPathRenderer.h" |
41 | #include "src/gpu/GrProxyProvider.h" |
42 | #include "src/gpu/GrRenderTarget.h" |
43 | #include "src/gpu/GrRenderTargetContextPriv.h" |
44 | #include "src/gpu/GrResourceProvider.h" |
45 | #include "src/gpu/GrStencilAttachment.h" |
46 | #include "src/gpu/GrStyle.h" |
47 | #include "src/gpu/GrTracing.h" |
48 | #include "src/gpu/SkGr.h" |
49 | #include "src/gpu/effects/GrBicubicEffect.h" |
50 | #include "src/gpu/effects/GrRRectEffect.h" |
51 | #include "src/gpu/geometry/GrQuad.h" |
52 | #include "src/gpu/geometry/GrQuadUtils.h" |
53 | #include "src/gpu/geometry/GrStyledShape.h" |
54 | #include "src/gpu/ops/GrAtlasTextOp.h" |
55 | #include "src/gpu/ops/GrClearOp.h" |
56 | #include "src/gpu/ops/GrDrawAtlasOp.h" |
57 | #include "src/gpu/ops/GrDrawOp.h" |
58 | #include "src/gpu/ops/GrDrawVerticesOp.h" |
59 | #include "src/gpu/ops/GrDrawableOp.h" |
60 | #include "src/gpu/ops/GrFillRRectOp.h" |
61 | #include "src/gpu/ops/GrFillRectOp.h" |
62 | #include "src/gpu/ops/GrLatticeOp.h" |
63 | #include "src/gpu/ops/GrOp.h" |
64 | #include "src/gpu/ops/GrOvalOpFactory.h" |
65 | #include "src/gpu/ops/GrRegionOp.h" |
66 | #include "src/gpu/ops/GrShadowRRectOp.h" |
67 | #include "src/gpu/ops/GrStencilPathOp.h" |
68 | #include "src/gpu/ops/GrStrokeRectOp.h" |
69 | #include "src/gpu/ops/GrTextureOp.h" |
70 | #include "src/gpu/text/GrSDFTOptions.h" |
71 | #include "src/gpu/text/GrTextBlobCache.h" |
72 | |
73 | #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this->drawingManager()->getContext()) |
74 | #define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(this->singleOwner()) |
75 | #define ASSERT_SINGLE_OWNER_PRIV GR_ASSERT_SINGLE_OWNER(fRenderTargetContext->singleOwner()) |
76 | #define RETURN_IF_ABANDONED if (fContext->abandoned()) { return; } |
77 | #define RETURN_IF_ABANDONED_PRIV if (fRenderTargetContext->fContext->abandoned()) { return; } |
78 | #define RETURN_FALSE_IF_ABANDONED if (fContext->abandoned()) { return false; } |
79 | #define RETURN_FALSE_IF_ABANDONED_PRIV if (fRenderTargetContext->fContext->abandoned()) { return false; } |
80 | #define RETURN_NULL_IF_ABANDONED if (fContext->abandoned()) { return nullptr; } |
81 | |
82 | ////////////////////////////////////////////////////////////////////////////// |
83 | |
84 | class AutoCheckFlush { |
85 | public: |
86 | AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) { |
87 | SkASSERT(fDrawingManager); |
88 | } |
89 | ~AutoCheckFlush() { fDrawingManager->flushIfNecessary(); } |
90 | |
91 | private: |
92 | GrDrawingManager* fDrawingManager; |
93 | }; |
94 | |
95 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::Make( |
96 | GrRecordingContext* context, |
97 | GrColorType colorType, |
98 | sk_sp<SkColorSpace> colorSpace, |
99 | sk_sp<GrSurfaceProxy> proxy, |
100 | GrSurfaceOrigin origin, |
101 | const SkSurfaceProps* surfaceProps, |
102 | bool managedOps) { |
103 | if (!proxy) { |
104 | return nullptr; |
105 | } |
106 | |
107 | const GrBackendFormat& format = proxy->backendFormat(); |
108 | GrSwizzle readSwizzle, writeSwizzle; |
109 | if (colorType != GrColorType::kUnknown) { |
110 | readSwizzle = context->priv().caps()->getReadSwizzle(format, colorType); |
111 | writeSwizzle = context->priv().caps()->getWriteSwizzle(format, colorType); |
112 | } |
113 | |
114 | GrSurfaceProxyView readView(proxy, origin, readSwizzle); |
115 | GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle); |
116 | |
117 | return std::make_unique<GrRenderTargetContext>(context, std::move(readView), |
118 | std::move(writeView), colorType, |
119 | std::move(colorSpace), surfaceProps, managedOps); |
120 | } |
121 | |
122 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::Make( |
123 | GrRecordingContext* context, |
124 | GrColorType colorType, |
125 | sk_sp<SkColorSpace> colorSpace, |
126 | SkBackingFit fit, |
127 | SkISize dimensions, |
128 | const GrBackendFormat& format, |
129 | int sampleCnt, |
130 | GrMipmapped mipMapped, |
131 | GrProtected isProtected, |
132 | GrSurfaceOrigin origin, |
133 | SkBudgeted budgeted, |
134 | const SkSurfaceProps* surfaceProps) { |
135 | // It is probably not necessary to check if the context is abandoned here since uses of the |
136 | // GrRenderTargetContext which need the context will mostly likely fail later on without an |
137 | // issue. However having this hear adds some reassurance in case there is a path doesn't handle |
138 | // an abandoned context correctly. It also lets us early out of some extra work. |
139 | if (context->abandoned()) { |
140 | return nullptr; |
141 | } |
142 | |
143 | sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy( |
144 | format, dimensions, GrRenderable::kYes, sampleCnt, mipMapped, fit, budgeted, |
145 | isProtected); |
146 | if (!proxy) { |
147 | return nullptr; |
148 | } |
149 | |
150 | auto rtc = GrRenderTargetContext::Make(context, colorType, std::move(colorSpace), |
151 | std::move(proxy), origin, surfaceProps, true); |
152 | if (!rtc) { |
153 | return nullptr; |
154 | } |
155 | rtc->discard(); |
156 | return rtc; |
157 | } |
158 | |
159 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::Make( |
160 | GrRecordingContext* context, |
161 | GrColorType colorType, |
162 | sk_sp<SkColorSpace> colorSpace, |
163 | SkBackingFit fit, |
164 | SkISize dimensions, |
165 | int sampleCnt, |
166 | GrMipmapped mipMapped, |
167 | GrProtected isProtected, |
168 | GrSurfaceOrigin origin, |
169 | SkBudgeted budgeted, |
170 | const SkSurfaceProps* surfaceProps) { |
171 | auto format = context->priv().caps()->getDefaultBackendFormat(colorType, GrRenderable::kYes); |
172 | if (!format.isValid()) { |
173 | return nullptr; |
174 | } |
175 | |
176 | return GrRenderTargetContext::Make(context, colorType, std::move(colorSpace), fit, dimensions, |
177 | format, sampleCnt, mipMapped, isProtected, origin, budgeted, |
178 | surfaceProps); |
179 | } |
180 | |
181 | static inline GrColorType color_type_fallback(GrColorType ct) { |
182 | switch (ct) { |
183 | // kRGBA_8888 is our default fallback for many color types that may not have renderable |
184 | // backend formats. |
185 | case GrColorType::kAlpha_8: |
186 | case GrColorType::kBGR_565: |
187 | case GrColorType::kABGR_4444: |
188 | case GrColorType::kBGRA_8888: |
189 | case GrColorType::kRGBA_1010102: |
190 | case GrColorType::kBGRA_1010102: |
191 | case GrColorType::kRGBA_F16: |
192 | case GrColorType::kRGBA_F16_Clamped: |
193 | return GrColorType::kRGBA_8888; |
194 | case GrColorType::kAlpha_F16: |
195 | return GrColorType::kRGBA_F16; |
196 | case GrColorType::kGray_8: |
197 | return GrColorType::kRGB_888x; |
198 | default: |
199 | return GrColorType::kUnknown; |
200 | } |
201 | } |
202 | |
203 | std::tuple<GrColorType, GrBackendFormat> GrRenderTargetContext::GetFallbackColorTypeAndFormat( |
204 | GrImageContext* context, GrColorType colorType, int sampleCnt) { |
205 | auto caps = context->priv().caps(); |
206 | do { |
207 | auto format = caps->getDefaultBackendFormat(colorType, GrRenderable::kYes); |
208 | // We continue to the fallback color type if there no default renderable format or we |
209 | // requested msaa and the format doesn't support msaa. |
210 | if (format.isValid() && caps->isFormatRenderable(format, sampleCnt)) { |
211 | return {colorType, format}; |
212 | } |
213 | colorType = color_type_fallback(colorType); |
214 | } while (colorType != GrColorType::kUnknown); |
215 | return {GrColorType::kUnknown, {}}; |
216 | } |
217 | |
218 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::MakeWithFallback( |
219 | GrRecordingContext* context, |
220 | GrColorType colorType, |
221 | sk_sp<SkColorSpace> colorSpace, |
222 | SkBackingFit fit, |
223 | SkISize dimensions, |
224 | int sampleCnt, |
225 | GrMipmapped mipMapped, |
226 | GrProtected isProtected, |
227 | GrSurfaceOrigin origin, |
228 | SkBudgeted budgeted, |
229 | const SkSurfaceProps* surfaceProps) { |
230 | auto [ct, format] = GetFallbackColorTypeAndFormat(context, colorType, sampleCnt); |
231 | if (ct == GrColorType::kUnknown) { |
232 | return nullptr; |
233 | } |
234 | return GrRenderTargetContext::Make(context, ct, colorSpace, fit, dimensions, sampleCnt, |
235 | mipMapped, isProtected, origin, budgeted, surfaceProps); |
236 | } |
237 | |
238 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::MakeFromBackendTexture( |
239 | GrRecordingContext* context, |
240 | GrColorType colorType, |
241 | sk_sp<SkColorSpace> colorSpace, |
242 | const GrBackendTexture& tex, |
243 | int sampleCnt, |
244 | GrSurfaceOrigin origin, |
245 | const SkSurfaceProps* surfaceProps, |
246 | sk_sp<GrRefCntedCallback> releaseHelper) { |
247 | SkASSERT(sampleCnt > 0); |
248 | sk_sp<GrTextureProxy> proxy(context->priv().proxyProvider()->wrapRenderableBackendTexture( |
249 | tex, sampleCnt, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, |
250 | std::move(releaseHelper))); |
251 | if (!proxy) { |
252 | return nullptr; |
253 | } |
254 | |
255 | return GrRenderTargetContext::Make(context, colorType, std::move(colorSpace), std::move(proxy), |
256 | origin, surfaceProps); |
257 | } |
258 | |
259 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::MakeFromBackendTextureAsRenderTarget( |
260 | GrRecordingContext* context, |
261 | GrColorType colorType, |
262 | sk_sp<SkColorSpace> colorSpace, |
263 | const GrBackendTexture& tex, |
264 | int sampleCnt, |
265 | GrSurfaceOrigin origin, |
266 | const SkSurfaceProps* surfaceProps) { |
267 | SkASSERT(sampleCnt > 0); |
268 | sk_sp<GrSurfaceProxy> proxy( |
269 | context->priv().proxyProvider()->wrapBackendTextureAsRenderTarget(tex, sampleCnt)); |
270 | if (!proxy) { |
271 | return nullptr; |
272 | } |
273 | |
274 | return GrRenderTargetContext::Make(context, colorType, std::move(colorSpace), std::move(proxy), |
275 | origin, surfaceProps); |
276 | } |
277 | |
278 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::MakeFromBackendRenderTarget( |
279 | GrRecordingContext* context, |
280 | GrColorType colorType, |
281 | sk_sp<SkColorSpace> colorSpace, |
282 | const GrBackendRenderTarget& rt, |
283 | GrSurfaceOrigin origin, |
284 | const SkSurfaceProps* surfaceProps, |
285 | ReleaseProc releaseProc, |
286 | ReleaseContext releaseCtx) { |
287 | sk_sp<GrRefCntedCallback> releaseHelper; |
288 | if (releaseProc) { |
289 | releaseHelper.reset(new GrRefCntedCallback(releaseProc, releaseCtx)); |
290 | } |
291 | |
292 | sk_sp<GrSurfaceProxy> proxy( |
293 | context->priv().proxyProvider()->wrapBackendRenderTarget(rt, std::move(releaseHelper))); |
294 | if (!proxy) { |
295 | return nullptr; |
296 | } |
297 | |
298 | return GrRenderTargetContext::Make(context, colorType, std::move(colorSpace), std::move(proxy), |
299 | origin, surfaceProps); |
300 | } |
301 | |
302 | std::unique_ptr<GrRenderTargetContext> GrRenderTargetContext::MakeFromVulkanSecondaryCB( |
303 | GrRecordingContext* context, |
304 | const SkImageInfo& imageInfo, |
305 | const GrVkDrawableInfo& vkInfo, |
306 | const SkSurfaceProps* props) { |
307 | sk_sp<GrSurfaceProxy> proxy( |
308 | context->priv().proxyProvider()->wrapVulkanSecondaryCBAsRenderTarget(imageInfo, |
309 | vkInfo)); |
310 | if (!proxy) { |
311 | return nullptr; |
312 | } |
313 | |
314 | return GrRenderTargetContext::Make(context, SkColorTypeToGrColorType(imageInfo.colorType()), |
315 | imageInfo.refColorSpace(), std::move(proxy), |
316 | kTopLeft_GrSurfaceOrigin, props); |
317 | } |
318 | |
319 | // In MDB mode the reffing of the 'getLastOpsTask' call's result allows in-progress |
320 | // GrOpsTask to be picked up and added to by renderTargetContexts lower in the call |
321 | // stack. When this occurs with a closed GrOpsTask, a new one will be allocated |
322 | // when the renderTargetContext attempts to use it (via getOpsTask). |
323 | GrRenderTargetContext::GrRenderTargetContext(GrRecordingContext* context, |
324 | GrSurfaceProxyView readView, |
325 | GrSurfaceProxyView writeView, |
326 | GrColorType colorType, |
327 | sk_sp<SkColorSpace> colorSpace, |
328 | const SkSurfaceProps* surfaceProps, |
329 | bool managedOpsTask) |
330 | : GrSurfaceContext(context, std::move(readView), colorType, kPremul_SkAlphaType, |
331 | std::move(colorSpace)) |
332 | , fWriteView(std::move(writeView)) |
333 | , fSurfaceProps(SkSurfacePropsCopyOrDefault(surfaceProps)) |
334 | , fManagedOpsTask(managedOpsTask) |
335 | , fGlyphPainter(*this) { |
336 | fOpsTask = sk_ref_sp(context->priv().drawingManager()->getLastOpsTask(this->asSurfaceProxy())); |
337 | if (fOpsTask) { |
338 | fOpsTask->addClosedObserver(this); |
339 | } |
340 | SkASSERT(this->asSurfaceProxy() == fWriteView.proxy()); |
341 | SkASSERT(this->origin() == fWriteView.origin()); |
342 | |
343 | SkDEBUGCODE(this->validate();) |
344 | } |
345 | |
346 | #ifdef SK_DEBUG |
347 | void GrRenderTargetContext::onValidate() const { |
348 | if (fOpsTask && !fOpsTask->isClosed()) { |
349 | SkASSERT(this->drawingManager()->getLastRenderTask(fWriteView.proxy()) == fOpsTask.get()); |
350 | } |
351 | } |
352 | #endif |
353 | |
354 | GrRenderTargetContext::~GrRenderTargetContext() { |
355 | ASSERT_SINGLE_OWNER |
356 | if (fOpsTask) { |
357 | fOpsTask->removeClosedObserver(this); |
358 | } |
359 | } |
360 | |
361 | inline GrAAType GrRenderTargetContext::chooseAAType(GrAA aa) { |
362 | if (GrAA::kNo == aa) { |
363 | // On some devices we cannot disable MSAA if it is enabled so we make the AA type reflect |
364 | // that. |
365 | if (this->numSamples() > 1 && !this->caps()->multisampleDisableSupport()) { |
366 | return GrAAType::kMSAA; |
367 | } |
368 | return GrAAType::kNone; |
369 | } |
370 | return (this->numSamples() > 1) ? GrAAType::kMSAA : GrAAType::kCoverage; |
371 | } |
372 | |
373 | GrMipmapped GrRenderTargetContext::mipmapped() const { |
374 | if (const GrTextureProxy* proxy = this->asTextureProxy()) { |
375 | return proxy->mipmapped(); |
376 | } |
377 | return GrMipmapped::kNo; |
378 | } |
379 | |
380 | GrOpsTask* GrRenderTargetContext::getOpsTask() { |
381 | ASSERT_SINGLE_OWNER |
382 | SkDEBUGCODE(this->validate();) |
383 | |
384 | if (!fOpsTask) { |
385 | sk_sp<GrOpsTask> newOpsTask = |
386 | this->drawingManager()->newOpsTask(this->writeSurfaceView(), fManagedOpsTask); |
387 | if (fOpsTask && fNumStencilSamples > 0) { |
388 | // Store the stencil values in memory upon completion of fOpsTask. |
389 | fOpsTask->setMustPreserveStencil(); |
390 | // Reload the stencil buffer content at the beginning of newOpsTask. |
391 | // FIXME: Could the topo sort insert a task between these two that modifies the stencil |
392 | // values? |
393 | newOpsTask->setInitialStencilContent(GrOpsTask::StencilContent::kPreserved); |
394 | } |
395 | newOpsTask->addClosedObserver(this); |
396 | fOpsTask = std::move(newOpsTask); |
397 | } |
398 | SkASSERT(!fOpsTask->isClosed()); |
399 | return fOpsTask.get(); |
400 | } |
401 | |
402 | static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) { |
403 | SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint); |
404 | if (lcd) { |
405 | // This is the correct computation for canonicalColor, but there are tons of cases where LCD |
406 | // can be modified. For now we just regenerate if any run in a textblob has LCD. |
407 | // TODO figure out where all of these modifications are and see if we can incorporate that |
408 | // logic at a higher level *OR* use sRGB |
409 | //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor); |
410 | |
411 | // TODO we want to figure out a way to be able to use the canonical color on LCD text, |
412 | // see the note above. We pick a dummy value for LCD text to ensure we always match the |
413 | // same key |
414 | return SK_ColorTRANSPARENT; |
415 | } else { |
416 | // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have |
417 | // gamma corrected masks anyways, nor color |
418 | U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor), |
419 | SkColorGetG(canonicalColor), |
420 | SkColorGetB(canonicalColor)); |
421 | // reduce to our finite number of bits |
422 | canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum)); |
423 | } |
424 | return canonicalColor; |
425 | } |
426 | |
427 | void GrRenderTargetContext::drawGlyphRunList(const GrClip* clip, |
428 | const SkMatrixProvider& viewMatrix, |
429 | const SkGlyphRunList& glyphRunList) { |
430 | ASSERT_SINGLE_OWNER |
431 | RETURN_IF_ABANDONED |
432 | SkDEBUGCODE(this->validate();) |
433 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawGlyphRunList" , fContext); |
434 | |
435 | // Drawing text can cause us to do inline uploads. This is not supported for wrapped vulkan |
436 | // secondary command buffers because it would require stopping and starting a render pass which |
437 | // we don't have access to. |
438 | if (this->wrapsVkSecondaryCB()) { |
439 | return; |
440 | } |
441 | |
442 | GrSDFTOptions options = fContext->priv().SDFTOptions(); |
443 | GrTextBlobCache* textBlobCache = fContext->priv().getTextBlobCache(); |
444 | |
445 | // Get the first paint to use as the key paint. |
446 | const SkPaint& blobPaint = glyphRunList.paint(); |
447 | |
448 | SkPoint drawOrigin = glyphRunList.origin(); |
449 | |
450 | SkMaskFilterBase::BlurRec blurRec; |
451 | // It might be worth caching these things, but its not clear at this time |
452 | // TODO for animated mask filters, this will fill up our cache. We need a safeguard here |
453 | const SkMaskFilter* mf = blobPaint.getMaskFilter(); |
454 | bool canCache = glyphRunList.canCache() && |
455 | !(blobPaint.getPathEffect() || (mf && !as_MFB(mf)->asABlur(&blurRec))); |
456 | |
457 | // If we're doing linear blending, then we can disable the gamma hacks. |
458 | // Otherwise, leave them on. In either case, we still want the contrast boost: |
459 | // TODO: Can we be even smarter about mask gamma based on the dest transfer function? |
460 | SkScalerContextFlags scalerContextFlags = this->colorInfo().isLinearlyBlended() |
461 | ? SkScalerContextFlags::kBoostContrast |
462 | : SkScalerContextFlags::kFakeGammaAndBoostContrast; |
463 | |
464 | sk_sp<GrTextBlob> blob; |
465 | GrTextBlob::Key key; |
466 | if (canCache) { |
467 | bool hasLCD = glyphRunList.anyRunsLCD(); |
468 | |
469 | // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry |
470 | SkPixelGeometry pixelGeometry = |
471 | hasLCD ? fSurfaceProps.pixelGeometry() : kUnknown_SkPixelGeometry; |
472 | |
473 | GrColor canonicalColor = compute_canonical_color(blobPaint, hasLCD); |
474 | |
475 | key.fPixelGeometry = pixelGeometry; |
476 | key.fUniqueID = glyphRunList.uniqueID(); |
477 | key.fStyle = blobPaint.getStyle(); |
478 | key.fHasBlur = SkToBool(mf); |
479 | key.fCanonicalColor = canonicalColor; |
480 | key.fScalerContextFlags = scalerContextFlags; |
481 | blob = textBlobCache->find(key); |
482 | } |
483 | |
484 | const SkMatrix& drawMatrix(viewMatrix.localToDevice()); |
485 | if (blob == nullptr || !blob->canReuse(blobPaint, blurRec, drawMatrix, drawOrigin)) { |
486 | if (blob != nullptr) { |
487 | // We have to remake the blob because changes may invalidate our masks. |
488 | // TODO we could probably get away with reuse most of the time if the pointer is unique, |
489 | // but we'd have to clear the SubRun information |
490 | textBlobCache->remove(blob.get()); |
491 | } |
492 | if (canCache) { |
493 | blob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, drawMatrix); |
494 | } else { |
495 | blob = GrTextBlob::Make(glyphRunList, drawMatrix); |
496 | } |
497 | bool supportsSDFT = fContext->priv().caps()->shaderCaps()->supportsDistanceFieldText(); |
498 | fGlyphPainter.processGlyphRunList( |
499 | glyphRunList, drawMatrix, fSurfaceProps, supportsSDFT, options, blob.get()); |
500 | } |
501 | |
502 | for (GrSubRun* subRun : blob->subRunList()) { |
503 | subRun->draw(clip, viewMatrix, glyphRunList, this); |
504 | } |
505 | } |
506 | |
507 | void GrRenderTargetContext::discard() { |
508 | ASSERT_SINGLE_OWNER |
509 | RETURN_IF_ABANDONED |
510 | SkDEBUGCODE(this->validate();) |
511 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "discard" , fContext); |
512 | |
513 | AutoCheckFlush acf(this->drawingManager()); |
514 | |
515 | this->getOpsTask()->discard(); |
516 | } |
517 | |
518 | static void clear_to_grpaint(const SkPMColor4f& color, GrPaint* paint) { |
519 | paint->setColor4f(color); |
520 | if (color.isOpaque()) { |
521 | // Can just rely on the src-over blend mode to do the right thing |
522 | paint->setPorterDuffXPFactory(SkBlendMode::kSrcOver); |
523 | } else { |
524 | // A clear overwrites the prior color, so even if it's transparent, it behaves as if it |
525 | // were src blended |
526 | paint->setPorterDuffXPFactory(SkBlendMode::kSrc); |
527 | } |
528 | } |
529 | |
530 | // NOTE: We currently pass the premul color unmodified to the gpu, since we assume the GrRTC has a |
531 | // premul alpha type. If we ever support different alpha type render targets, this function should |
532 | // transform the color as appropriate. |
533 | void GrRenderTargetContext::internalClear(const SkIRect* scissor, |
534 | const SkPMColor4f& color, |
535 | bool upgradePartialToFull) { |
536 | ASSERT_SINGLE_OWNER |
537 | RETURN_IF_ABANDONED |
538 | SkDEBUGCODE(this->validate();) |
539 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "clear" , fContext); |
540 | |
541 | // There are three ways clears are handled: load ops, native clears, and draws. Load ops are |
542 | // only for fullscreen clears; native clears can be fullscreen or with scissors if the backend |
543 | // supports then. Drawing an axis-aligned rect is the fallback path. |
544 | GrScissorState scissorState(this->asSurfaceProxy()->backingStoreDimensions()); |
545 | if (scissor && !scissorState.set(*scissor)) { |
546 | // The clear is offscreen, so skip it (normally this would be handled by addDrawOp, |
547 | // except clear ops are not draw ops). |
548 | return; |
549 | } |
550 | |
551 | // If we have a scissor but it's okay to clear beyond it for performance reasons, then disable |
552 | // the test. We only do this when the clear would be handled by a load op or natively. |
553 | if (scissorState.enabled() && !this->caps()->performColorClearsAsDraws()) { |
554 | if (upgradePartialToFull && (this->caps()->preferFullscreenClears() || |
555 | this->caps()->shouldInitializeTextures())) { |
556 | // TODO: wrt the shouldInitializeTextures path, it would be more performant to |
557 | // only clear the entire target if we knew it had not been cleared before. As |
558 | // is this could end up doing a lot of redundant clears. |
559 | scissorState.setDisabled(); |
560 | } else { |
561 | // Unlike with stencil clears, we also allow clears up to the logical dimensions of the |
562 | // render target to overflow into any approx-fit padding of the backing store dimensions |
563 | scissorState.relaxTest(this->dimensions()); |
564 | } |
565 | } |
566 | |
567 | if (!scissorState.enabled()) { |
568 | // This is a fullscreen clear, so could be handled as a load op. Regardless, we can also |
569 | // discard all prior ops in the current task since the color buffer will be overwritten. |
570 | GrOpsTask* opsTask = this->getOpsTask(); |
571 | if (opsTask->resetForFullscreenClear(this->canDiscardPreviousOpsOnFullClear()) && |
572 | !this->caps()->performColorClearsAsDraws()) { |
573 | // The op list was emptied and native clears are allowed, so just use the load op |
574 | opsTask->setColorLoadOp(GrLoadOp::kClear, color); |
575 | return; |
576 | } else { |
577 | // Will use an op for the clear, reset the load op to discard since the op will |
578 | // blow away the color buffer contents |
579 | opsTask->setColorLoadOp(GrLoadOp::kDiscard); |
580 | } |
581 | } |
582 | |
583 | // At this point we are either a partial clear or a fullscreen clear that couldn't be applied |
584 | // as a load op. |
585 | bool clearAsDraw = this->caps()->performColorClearsAsDraws() || |
586 | (scissorState.enabled() && this->caps()->performPartialClearsAsDraws()); |
587 | if (clearAsDraw) { |
588 | GrPaint paint; |
589 | clear_to_grpaint(color, &paint); |
590 | this->addDrawOp(nullptr, |
591 | GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(), |
592 | SkRect::Make(scissorState.rect()))); |
593 | } else { |
594 | this->addOp(GrClearOp::MakeColor(fContext, scissorState, color)); |
595 | } |
596 | } |
597 | |
598 | void GrRenderTargetContext::drawPaint(const GrClip* clip, |
599 | GrPaint&& paint, |
600 | const SkMatrix& viewMatrix) { |
601 | // Start with the render target, since that is the maximum content we could possibly fill. |
602 | // drawFilledQuad() will automatically restrict it to clip bounds for us if possible. |
603 | SkRect r = this->asSurfaceProxy()->getBoundsRect(); |
604 | if (!paint.numTotalFragmentProcessors()) { |
605 | // The paint is trivial so we won't need to use local coordinates, so skip calculating the |
606 | // inverse view matrix. |
607 | this->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r, r); |
608 | } else { |
609 | // Use the inverse view matrix to arrive at appropriate local coordinates for the paint. |
610 | SkMatrix localMatrix; |
611 | if (!viewMatrix.invert(&localMatrix)) { |
612 | return; |
613 | } |
614 | this->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r, |
615 | localMatrix); |
616 | } |
617 | } |
618 | |
619 | enum class GrRenderTargetContext::QuadOptimization { |
620 | // The rect to draw doesn't intersect clip or render target, so no draw op should be added |
621 | kDiscarded, |
622 | // The rect to draw was converted to some other op and appended to the oplist, so no additional |
623 | // op is necessary. Currently this can convert it to a clear op or a rrect op. Only valid if |
624 | // a constColor is provided. |
625 | kSubmitted, |
626 | // The clip was folded into the device quad, with updated edge flags and local coords, and |
627 | // caller is responsible for adding an appropriate op. |
628 | kClipApplied, |
629 | // No change to clip, but quad updated to better fit clip/render target, and caller is |
630 | // responsible for adding an appropriate op. |
631 | kCropped |
632 | }; |
633 | |
634 | GrRenderTargetContext::QuadOptimization GrRenderTargetContext::attemptQuadOptimization( |
635 | const GrClip* clip, const SkPMColor4f* constColor, |
636 | const GrUserStencilSettings* stencilSettings, GrAA* aa, DrawQuad* quad) { |
637 | // Optimization requirements: |
638 | // 1. kDiscard applies when clip bounds and quad bounds do not intersect |
639 | // 2a. kSubmitted applies when constColor and final geom is pixel aligned rect; |
640 | // pixel aligned rect requires rect clip and (rect quad or quad covers clip) OR |
641 | // 2b. kSubmitted applies when constColor and rrect clip and quad covers clip |
642 | // 4. kClipApplied applies when rect clip and (rect quad or quad covers clip) |
643 | // 5. kCropped in all other scenarios (although a crop may be a no-op) |
644 | |
645 | // Save the old AA flags since CropToRect will modify 'quad' and if kCropped is returned, it's |
646 | // better to just keep the old flags instead of introducing mixed edge flags. |
647 | GrQuadAAFlags oldFlags = quad->fEdgeFlags; |
648 | |
649 | // Use the logical size of the render target, which allows for "fullscreen" clears even if |
650 | // the render target has an approximate backing fit |
651 | SkRect rtRect = this->asSurfaceProxy()->getBoundsRect(); |
652 | |
653 | SkRect drawBounds = quad->fDevice.bounds(); |
654 | if (!quad->fDevice.isFinite() || drawBounds.isEmpty() || |
655 | GrClip::IsOutsideClip(rtRect, drawBounds)) { |
656 | return QuadOptimization::kDiscarded; |
657 | } |
658 | auto conservativeCrop = [&]() { |
659 | static constexpr int kLargeDrawLimit = 15000; |
660 | // Crop the quad to the render target. This doesn't change the visual results of drawing but |
661 | // is meant to help numerical stability for excessively large draws. |
662 | if (drawBounds.width() > kLargeDrawLimit || drawBounds.height() > kLargeDrawLimit) { |
663 | GrQuadUtils::CropToRect(rtRect, *aa, quad, /* compute local */ !constColor); |
664 | SkASSERT(quad->fEdgeFlags == oldFlags); |
665 | } |
666 | }; |
667 | |
668 | bool simpleColor = !stencilSettings && constColor; |
669 | GrClip::PreClipResult result = clip ? clip->preApply(drawBounds, *aa) |
670 | : GrClip::PreClipResult(GrClip::Effect::kUnclipped); |
671 | switch(result.fEffect) { |
672 | case GrClip::Effect::kClippedOut: |
673 | return QuadOptimization::kDiscarded; |
674 | case GrClip::Effect::kUnclipped: |
675 | if (!simpleColor) { |
676 | conservativeCrop(); |
677 | return QuadOptimization::kClipApplied; |
678 | } else { |
679 | // Update result to store the render target bounds in order and then fall |
680 | // through to attempt the draw->native clear optimization |
681 | result = GrClip::PreClipResult(SkRRect::MakeRect(rtRect), *aa); |
682 | } |
683 | break; |
684 | case GrClip::Effect::kClipped: |
685 | if (!result.fIsRRect || (stencilSettings && result.fAA != *aa) || |
686 | (!result.fRRect.isRect() && !simpleColor)) { |
687 | // The clip and draw state are too complicated to try and reduce |
688 | conservativeCrop(); |
689 | return QuadOptimization::kCropped; |
690 | } // Else fall through to attempt to combine the draw and clip geometry together |
691 | break; |
692 | default: |
693 | SkUNREACHABLE; |
694 | } |
695 | |
696 | // If we reached here, we know we're an axis-aligned clip that is either a rect or a round rect, |
697 | // so we can potentially combine it with the draw geometry so that no clipping is needed. |
698 | SkASSERT(result.fEffect == GrClip::Effect::kClipped && result.fIsRRect); |
699 | SkRect clippedBounds = result.fRRect.getBounds(); |
700 | clippedBounds.intersect(rtRect); |
701 | if (result.fRRect.isRect()) { |
702 | // No rounded corners, so we might be able to become a native clear or we might be able to |
703 | // modify geometry and edge flags to represent intersected shape of clip and draw. |
704 | if (GrQuadUtils::CropToRect(clippedBounds, result.fAA, quad, |
705 | /*compute local*/ !constColor)) { |
706 | if (simpleColor && quad->fDevice.quadType() == GrQuad::Type::kAxisAligned) { |
707 | // Clear optimization is possible |
708 | drawBounds = quad->fDevice.bounds(); |
709 | if (drawBounds.contains(rtRect)) { |
710 | // Fullscreen clear |
711 | this->clear(*constColor); |
712 | return QuadOptimization::kSubmitted; |
713 | } else if (GrClip::IsPixelAligned(drawBounds) && |
714 | drawBounds.width() > 256 && drawBounds.height() > 256) { |
715 | // Scissor + clear (round shouldn't do anything since we are pixel aligned) |
716 | SkIRect scissorRect; |
717 | drawBounds.round(&scissorRect); |
718 | this->clear(scissorRect, *constColor); |
719 | return QuadOptimization::kSubmitted; |
720 | } |
721 | } |
722 | |
723 | // else the draw and clip were combined so just update the AA to reflect combination |
724 | if (*aa == GrAA::kNo && result.fAA == GrAA::kYes && |
725 | quad->fEdgeFlags != GrQuadAAFlags::kNone) { |
726 | // The clip was anti-aliased and now the draw needs to be upgraded to AA to |
727 | // properly reflect the smooth edge of the clip. |
728 | *aa = GrAA::kYes; |
729 | } |
730 | // We intentionally do not downgrade AA here because we don't know if we need to |
731 | // preserve MSAA (see GrQuadAAFlags docs). But later in the pipeline, the ops can |
732 | // use GrResolveAATypeForQuad() to turn off coverage AA when all flags are off. |
733 | // deviceQuad is exactly the intersection of original quad and clip, so it can be |
734 | // drawn with no clip (submitted by caller) |
735 | return QuadOptimization::kClipApplied; |
736 | } |
737 | } else { |
738 | // Rounded corners and constant filled color (limit ourselves to solid colors because |
739 | // there is no way to use custom local coordinates with drawRRect). |
740 | SkASSERT(simpleColor); |
741 | if (GrQuadUtils::CropToRect(clippedBounds, result.fAA, quad, |
742 | /* compute local */ false) && |
743 | quad->fDevice.quadType() == GrQuad::Type::kAxisAligned && |
744 | quad->fDevice.bounds().contains(clippedBounds)) { |
745 | // Since the cropped quad became a rectangle which covered the bounds of the rrect, |
746 | // we can draw the rrect directly and ignore the edge flags |
747 | GrPaint paint; |
748 | clear_to_grpaint(*constColor, &paint); |
749 | this->drawRRect(nullptr, std::move(paint), result.fAA, SkMatrix::I(), result.fRRect, |
750 | GrStyle::SimpleFill()); |
751 | return QuadOptimization::kSubmitted; |
752 | } |
753 | } |
754 | |
755 | // The quads have been updated to better fit the clip bounds, but can't get rid of |
756 | // the clip entirely |
757 | quad->fEdgeFlags = oldFlags; |
758 | return QuadOptimization::kCropped; |
759 | } |
760 | |
761 | void GrRenderTargetContext::drawFilledQuad(const GrClip* clip, |
762 | GrPaint&& paint, |
763 | GrAA aa, |
764 | DrawQuad* quad, |
765 | const GrUserStencilSettings* ss) { |
766 | ASSERT_SINGLE_OWNER |
767 | RETURN_IF_ABANDONED |
768 | SkDEBUGCODE(this->validate();) |
769 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawFilledQuad" , fContext); |
770 | |
771 | AutoCheckFlush acf(this->drawingManager()); |
772 | |
773 | SkPMColor4f* constColor = nullptr; |
774 | SkPMColor4f paintColor; |
775 | if (!ss && !paint.hasCoverageFragmentProcessor() && paint.isConstantBlendedColor(&paintColor)) { |
776 | // Only consider clears/rrects when it's easy to guarantee 100% fill with single color |
777 | constColor = &paintColor; |
778 | } |
779 | |
780 | QuadOptimization opt = this->attemptQuadOptimization(clip, constColor, ss, &aa, quad); |
781 | if (opt >= QuadOptimization::kClipApplied) { |
782 | // These optimizations require caller to add an op themselves |
783 | const GrClip* finalClip = opt == QuadOptimization::kClipApplied ? nullptr : clip; |
784 | GrAAType aaType = ss ? (aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone) |
785 | : this->chooseAAType(aa); |
786 | this->addDrawOp(finalClip, GrFillRectOp::Make(fContext, std::move(paint), aaType, |
787 | quad, ss)); |
788 | } |
789 | // All other optimization levels were completely handled inside attempt(), so no extra op needed |
790 | } |
791 | |
792 | void GrRenderTargetContext::drawTexturedQuad(const GrClip* clip, |
793 | GrSurfaceProxyView proxyView, |
794 | SkAlphaType srcAlphaType, |
795 | sk_sp<GrColorSpaceXform> textureXform, |
796 | GrSamplerState::Filter filter, |
797 | GrSamplerState::MipmapMode mm, |
798 | const SkPMColor4f& color, |
799 | SkBlendMode blendMode, |
800 | GrAA aa, |
801 | DrawQuad* quad, |
802 | const SkRect* subset) { |
803 | ASSERT_SINGLE_OWNER |
804 | RETURN_IF_ABANDONED |
805 | SkDEBUGCODE(this->validate();) |
806 | SkASSERT(proxyView.asTextureProxy()); |
807 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawTexturedQuad" , fContext); |
808 | |
809 | AutoCheckFlush acf(this->drawingManager()); |
810 | |
811 | // Functionally this is very similar to drawFilledQuad except that there's no constColor to |
812 | // enable the kSubmitted optimizations, no stencil settings support, and its a GrTextureOp. |
813 | QuadOptimization opt = this->attemptQuadOptimization(clip, nullptr, nullptr, &aa, quad); |
814 | |
815 | SkASSERT(opt != QuadOptimization::kSubmitted); |
816 | if (opt != QuadOptimization::kDiscarded) { |
817 | // And the texture op if not discarded |
818 | const GrClip* finalClip = opt == QuadOptimization::kClipApplied ? nullptr : clip; |
819 | GrAAType aaType = this->chooseAAType(aa); |
820 | auto clampType = GrColorTypeClampType(this->colorInfo().colorType()); |
821 | auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes |
822 | : GrTextureOp::Saturate::kNo; |
823 | // Use the provided subset, although hypothetically we could detect that the cropped local |
824 | // quad is sufficiently inside the subset and the constraint could be dropped. |
825 | this->addDrawOp(finalClip, |
826 | GrTextureOp::Make(fContext, std::move(proxyView), srcAlphaType, |
827 | std::move(textureXform), filter, mm, color, saturate, |
828 | blendMode, aaType, quad, subset)); |
829 | } |
830 | } |
831 | |
832 | void GrRenderTargetContext::drawRect(const GrClip* clip, |
833 | GrPaint&& paint, |
834 | GrAA aa, |
835 | const SkMatrix& viewMatrix, |
836 | const SkRect& rect, |
837 | const GrStyle* style) { |
838 | if (!style) { |
839 | style = &GrStyle::SimpleFill(); |
840 | } |
841 | ASSERT_SINGLE_OWNER |
842 | RETURN_IF_ABANDONED |
843 | SkDEBUGCODE(this->validate();) |
844 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawRect" , fContext); |
845 | |
846 | // Path effects should've been devolved to a path in SkGpuDevice |
847 | SkASSERT(!style->pathEffect()); |
848 | |
849 | AutoCheckFlush acf(this->drawingManager()); |
850 | |
851 | const SkStrokeRec& stroke = style->strokeRec(); |
852 | if (stroke.getStyle() == SkStrokeRec::kFill_Style) { |
853 | // Fills the rect, using rect as its own local coordinates |
854 | this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect); |
855 | return; |
856 | } else if ((stroke.getStyle() == SkStrokeRec::kStroke_Style || |
857 | stroke.getStyle() == SkStrokeRec::kHairline_Style) && |
858 | (rect.width() && rect.height())) { |
859 | // Only use the StrokeRectOp for non-empty rectangles. Empty rectangles will be processed by |
860 | // GrStyledShape to handle stroke caps and dashing properly. |
861 | std::unique_ptr<GrDrawOp> op; |
862 | |
863 | GrAAType aaType = this->chooseAAType(aa); |
864 | op = GrStrokeRectOp::Make(fContext, std::move(paint), aaType, viewMatrix, rect, stroke); |
865 | // op may be null if the stroke is not supported or if using coverage aa and the view matrix |
866 | // does not preserve rectangles. |
867 | if (op) { |
868 | this->addDrawOp(clip, std::move(op)); |
869 | return; |
870 | } |
871 | } |
872 | assert_alive(paint); |
873 | this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, |
874 | GrStyledShape(rect, *style)); |
875 | } |
876 | |
877 | void GrRenderTargetContext::drawQuadSet(const GrClip* clip, GrPaint&& paint, GrAA aa, |
878 | const SkMatrix& viewMatrix, const QuadSetEntry quads[], |
879 | int cnt) { |
880 | GrAAType aaType = this->chooseAAType(aa); |
881 | |
882 | GrFillRectOp::AddFillRectOps(this, clip, fContext, std::move(paint), aaType, viewMatrix, |
883 | quads, cnt); |
884 | } |
885 | |
886 | int GrRenderTargetContextPriv::maxWindowRectangles() const { |
887 | return fRenderTargetContext->asRenderTargetProxy()->maxWindowRectangles( |
888 | *fRenderTargetContext->caps()); |
889 | } |
890 | |
891 | GrOpsTask::CanDiscardPreviousOps GrRenderTargetContext::canDiscardPreviousOpsOnFullClear() const { |
892 | #if GR_TEST_UTILS |
893 | if (fPreserveOpsOnFullClear_TestingOnly) { |
894 | return GrOpsTask::CanDiscardPreviousOps::kNo; |
895 | } |
896 | #endif |
897 | // Regardless of how the clear is implemented (native clear or a fullscreen quad), all prior ops |
898 | // would normally be overwritten. The one exception is if the render target context is marked as |
899 | // needing a stencil buffer then there may be a prior op that writes to the stencil buffer. |
900 | // Although the clear will ignore the stencil buffer, following draw ops may not so we can't get |
901 | // rid of all the preceding ops. Beware! If we ever add any ops that have a side effect beyond |
902 | // modifying the stencil buffer we will need a more elaborate tracking system (skbug.com/7002). |
903 | return GrOpsTask::CanDiscardPreviousOps(!fNumStencilSamples); |
904 | } |
905 | |
906 | void GrRenderTargetContext::setNeedsStencil(bool useMixedSamplesIfNotMSAA) { |
907 | // Don't clear stencil until after we've changed fNumStencilSamples. This ensures we don't loop |
908 | // forever in the event that there are driver bugs and we need to clear as a draw. |
909 | bool hasInitializedStencil = fNumStencilSamples > 0; |
910 | |
911 | int numRequiredSamples = this->numSamples(); |
912 | if (useMixedSamplesIfNotMSAA && 1 == numRequiredSamples) { |
913 | SkASSERT(this->asRenderTargetProxy()->canUseMixedSamples(*this->caps())); |
914 | numRequiredSamples = this->caps()->internalMultisampleCount( |
915 | this->asSurfaceProxy()->backendFormat()); |
916 | } |
917 | SkASSERT(numRequiredSamples > 0); |
918 | |
919 | if (numRequiredSamples > fNumStencilSamples) { |
920 | fNumStencilSamples = numRequiredSamples; |
921 | this->asRenderTargetProxy()->setNeedsStencil(fNumStencilSamples); |
922 | } |
923 | |
924 | if (!hasInitializedStencil) { |
925 | if (this->caps()->performStencilClearsAsDraws()) { |
926 | // There is a driver bug with clearing stencil. We must use an op to manually clear the |
927 | // stencil buffer before the op that required 'setNeedsStencil'. |
928 | this->internalStencilClear(nullptr, /* inside mask */ false); |
929 | } else { |
930 | this->getOpsTask()->setInitialStencilContent( |
931 | GrOpsTask::StencilContent::kUserBitsCleared); |
932 | } |
933 | } |
934 | } |
935 | |
936 | void GrRenderTargetContext::internalStencilClear(const SkIRect* scissor, bool insideStencilMask) { |
937 | this->setNeedsStencil(/* useMixedSamplesIfNotMSAA = */ false); |
938 | |
939 | GrScissorState scissorState(this->asSurfaceProxy()->backingStoreDimensions()); |
940 | if (scissor && !scissorState.set(*scissor)) { |
941 | // The requested clear region is off screen, so nothing to do. |
942 | return; |
943 | } |
944 | |
945 | bool clearWithDraw = this->caps()->performStencilClearsAsDraws() || |
946 | (scissorState.enabled() && this->caps()->performPartialClearsAsDraws()); |
947 | if (clearWithDraw) { |
948 | const GrUserStencilSettings* ss = GrStencilSettings::SetClipBitSettings(insideStencilMask); |
949 | |
950 | // Configure the paint to have no impact on the color buffer |
951 | GrPaint paint; |
952 | paint.setXPFactory(GrDisableColorXPFactory::Get()); |
953 | this->addDrawOp(nullptr, |
954 | GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(), |
955 | SkRect::Make(scissorState.rect()), ss)); |
956 | } else { |
957 | this->addOp(GrClearOp::MakeStencilClip(fContext, scissorState, insideStencilMask)); |
958 | } |
959 | } |
960 | |
961 | void GrRenderTargetContextPriv::stencilPath(const GrHardClip* clip, |
962 | GrAA doStencilMSAA, |
963 | const SkMatrix& viewMatrix, |
964 | sk_sp<const GrPath> path) { |
965 | ASSERT_SINGLE_OWNER_PRIV |
966 | RETURN_IF_ABANDONED_PRIV |
967 | SkDEBUGCODE(fRenderTargetContext->validate();) |
968 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv" , "stencilPath" , |
969 | fRenderTargetContext->fContext); |
970 | |
971 | // TODO: extract portions of checkDraw that are relevant to path stenciling. |
972 | SkASSERT(path); |
973 | SkASSERT(fRenderTargetContext->caps()->shaderCaps()->pathRenderingSupport()); |
974 | |
975 | // FIXME: Use path bounds instead of this WAR once |
976 | // https://bugs.chromium.org/p/skia/issues/detail?id=5640 is resolved. |
977 | SkIRect bounds = SkIRect::MakeSize(fRenderTargetContext->dimensions()); |
978 | |
979 | // Setup clip and reject offscreen paths; we do this explicitly instead of relying on addDrawOp |
980 | // because GrStencilPathOp is not a draw op as its state depends directly on the choices made |
981 | // during this clip application. |
982 | GrAppliedHardClip appliedClip(fRenderTargetContext->dimensions(), |
983 | fRenderTargetContext->asSurfaceProxy()->backingStoreDimensions()); |
984 | |
985 | if (clip && GrClip::Effect::kClippedOut == clip->apply(&appliedClip, &bounds)) { |
986 | return; |
987 | } |
988 | // else see FIXME above; we'd normally want to check path bounds with render target bounds, |
989 | // but as it is, we're just using the full render target so intersecting the two bounds would |
990 | // do nothing. |
991 | |
992 | std::unique_ptr<GrOp> op = GrStencilPathOp::Make(fRenderTargetContext->fContext, |
993 | viewMatrix, |
994 | GrAA::kYes == doStencilMSAA, |
995 | appliedClip.hasStencilClip(), |
996 | appliedClip.scissorState(), |
997 | std::move(path)); |
998 | if (!op) { |
999 | return; |
1000 | } |
1001 | op->setClippedBounds(SkRect::Make(bounds)); |
1002 | |
1003 | fRenderTargetContext->setNeedsStencil(GrAA::kYes == doStencilMSAA); |
1004 | fRenderTargetContext->addOp(std::move(op)); |
1005 | } |
1006 | |
1007 | void GrRenderTargetContext::drawTextureSet(const GrClip* clip, |
1008 | TextureSetEntry set[], |
1009 | int cnt, |
1010 | int proxyRunCnt, |
1011 | GrSamplerState::Filter filter, |
1012 | GrSamplerState::MipmapMode mm, |
1013 | SkBlendMode mode, |
1014 | GrAA aa, |
1015 | SkCanvas::SrcRectConstraint constraint, |
1016 | const SkMatrix& viewMatrix, |
1017 | sk_sp<GrColorSpaceXform> texXform) { |
1018 | ASSERT_SINGLE_OWNER |
1019 | RETURN_IF_ABANDONED |
1020 | SkDEBUGCODE(this->validate();) |
1021 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawTextureSet" , fContext); |
1022 | |
1023 | // Create the minimum number of GrTextureOps needed to draw this set. Individual |
1024 | // GrTextureOps can rebind the texture between draws thus avoiding GrPaint (re)creation. |
1025 | AutoCheckFlush acf(this->drawingManager()); |
1026 | GrAAType aaType = this->chooseAAType(aa); |
1027 | auto clampType = GrColorTypeClampType(this->colorInfo().colorType()); |
1028 | auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes |
1029 | : GrTextureOp::Saturate::kNo; |
1030 | GrTextureOp::AddTextureSetOps(this, clip, fContext, set, cnt, proxyRunCnt, filter, mm, saturate, |
1031 | mode, aaType, constraint, viewMatrix, std::move(texXform)); |
1032 | } |
1033 | |
1034 | void GrRenderTargetContext::drawVertices(const GrClip* clip, |
1035 | GrPaint&& paint, |
1036 | const SkMatrixProvider& matrixProvider, |
1037 | sk_sp<SkVertices> vertices, |
1038 | GrPrimitiveType* overridePrimType, |
1039 | const SkRuntimeEffect* effect) { |
1040 | ASSERT_SINGLE_OWNER |
1041 | RETURN_IF_ABANDONED |
1042 | SkDEBUGCODE(this->validate();) |
1043 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawVertices" , fContext); |
1044 | |
1045 | AutoCheckFlush acf(this->drawingManager()); |
1046 | |
1047 | SkASSERT(vertices); |
1048 | GrAAType aaType = this->chooseAAType(GrAA::kNo); |
1049 | std::unique_ptr<GrDrawOp> op = |
1050 | GrDrawVerticesOp::Make(fContext, std::move(paint), std::move(vertices), matrixProvider, |
1051 | aaType, this->colorInfo().refColorSpaceXformFromSRGB(), |
1052 | overridePrimType, effect); |
1053 | this->addDrawOp(clip, std::move(op)); |
1054 | } |
1055 | |
1056 | /////////////////////////////////////////////////////////////////////////////// |
1057 | |
1058 | void GrRenderTargetContext::drawAtlas(const GrClip* clip, |
1059 | GrPaint&& paint, |
1060 | const SkMatrix& viewMatrix, |
1061 | int spriteCount, |
1062 | const SkRSXform xform[], |
1063 | const SkRect texRect[], |
1064 | const SkColor colors[]) { |
1065 | ASSERT_SINGLE_OWNER |
1066 | RETURN_IF_ABANDONED |
1067 | SkDEBUGCODE(this->validate();) |
1068 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawAtlas" , fContext); |
1069 | |
1070 | AutoCheckFlush acf(this->drawingManager()); |
1071 | |
1072 | GrAAType aaType = this->chooseAAType(GrAA::kNo); |
1073 | std::unique_ptr<GrDrawOp> op = GrDrawAtlasOp::Make(fContext, std::move(paint), viewMatrix, |
1074 | aaType, spriteCount, xform, texRect, colors); |
1075 | this->addDrawOp(clip, std::move(op)); |
1076 | } |
1077 | |
1078 | /////////////////////////////////////////////////////////////////////////////// |
1079 | |
1080 | void GrRenderTargetContext::drawRRect(const GrClip* origClip, |
1081 | GrPaint&& paint, |
1082 | GrAA aa, |
1083 | const SkMatrix& viewMatrix, |
1084 | const SkRRect& rrect, |
1085 | const GrStyle& style) { |
1086 | ASSERT_SINGLE_OWNER |
1087 | RETURN_IF_ABANDONED |
1088 | SkDEBUGCODE(this->validate();) |
1089 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawRRect" , fContext); |
1090 | |
1091 | SkASSERT(!style.pathEffect()); // this should've been devolved to a path in SkGpuDevice |
1092 | |
1093 | const SkStrokeRec& stroke = style.strokeRec(); |
1094 | if (stroke.getStyle() == SkStrokeRec::kFill_Style && rrect.isEmpty()) { |
1095 | return; |
1096 | } |
1097 | |
1098 | const GrClip* clip = origClip; |
1099 | // It is not uncommon to clip to a round rect and then draw that same round rect. Since our |
1100 | // lower level clip code works from op bounds, which are SkRects, it doesn't detect that the |
1101 | // clip can be ignored. The following test attempts to mitigate the stencil clip cost but only |
1102 | // works for axis-aligned round rects. This also only works for filled rrects since the stroke |
1103 | // width outsets beyond the rrect itself. |
1104 | // TODO: skbug.com/10462 - There was mixed performance wins and regressions when this |
1105 | // optimization was turned on outside of Android Framework. I (michaelludwig) believe this is |
1106 | // do to the overhead in determining if an SkClipStack is just a rrect. Once that is improved, |
1107 | // re-enable this and see if we avoid the regressions. |
1108 | #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK |
1109 | SkRRect devRRect; |
1110 | if (clip && stroke.getStyle() == SkStrokeRec::kFill_Style && |
1111 | rrect.transform(viewMatrix, &devRRect)) { |
1112 | GrClip::PreClipResult result = clip->preApply(devRRect.getBounds(), aa); |
1113 | switch(result.fEffect) { |
1114 | case GrClip::Effect::kClippedOut: |
1115 | return; |
1116 | case GrClip::Effect::kUnclipped: |
1117 | clip = nullptr; |
1118 | break; |
1119 | case GrClip::Effect::kClipped: |
1120 | // Currently there's no general-purpose rrect-to-rrect contains function, and if we |
1121 | // got here, we know the devRRect's bounds aren't fully contained by the clip. |
1122 | // Testing for equality between the two is a reasonable stop-gap for now. |
1123 | if (result.fIsRRect && result.fRRect == devRRect) { |
1124 | // NOTE: On the android framework, we allow this optimization even when the clip |
1125 | // is non-AA and the draw is AA. |
1126 | if (result.fAA == aa || (result.fAA == GrAA::kNo && aa == GrAA::kYes)) { |
1127 | clip = nullptr; |
1128 | } |
1129 | } |
1130 | break; |
1131 | default: |
1132 | SkUNREACHABLE; |
1133 | } |
1134 | } |
1135 | #endif |
1136 | |
1137 | AutoCheckFlush acf(this->drawingManager()); |
1138 | |
1139 | GrAAType aaType = this->chooseAAType(aa); |
1140 | |
1141 | std::unique_ptr<GrDrawOp> op; |
1142 | if (GrAAType::kCoverage == aaType && rrect.isSimple() && |
1143 | rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY && |
1144 | viewMatrix.rectStaysRect() && viewMatrix.isSimilarity()) { |
1145 | // In coverage mode, we draw axis-aligned circular roundrects with the GrOvalOpFactory |
1146 | // to avoid perf regressions on some platforms. |
1147 | assert_alive(paint); |
1148 | op = GrOvalOpFactory::MakeCircularRRectOp( |
1149 | fContext, std::move(paint), viewMatrix, rrect, stroke, this->caps()->shaderCaps()); |
1150 | } |
1151 | if (!op && style.isSimpleFill()) { |
1152 | assert_alive(paint); |
1153 | op = GrFillRRectOp::Make(fContext, std::move(paint), viewMatrix, rrect, aaType); |
1154 | } |
1155 | if (!op && GrAAType::kCoverage == aaType) { |
1156 | assert_alive(paint); |
1157 | op = GrOvalOpFactory::MakeRRectOp( |
1158 | fContext, std::move(paint), viewMatrix, rrect, stroke, this->caps()->shaderCaps()); |
1159 | } |
1160 | if (op) { |
1161 | this->addDrawOp(clip, std::move(op)); |
1162 | return; |
1163 | } |
1164 | |
1165 | assert_alive(paint); |
1166 | this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, |
1167 | GrStyledShape(rrect, style)); |
1168 | } |
1169 | |
1170 | /////////////////////////////////////////////////////////////////////////////// |
1171 | |
1172 | static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) { |
1173 | SkPoint3 result; |
1174 | m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX); |
1175 | result.fZ = pt.fZ; |
1176 | return result; |
1177 | } |
1178 | |
1179 | bool GrRenderTargetContext::drawFastShadow(const GrClip* clip, |
1180 | const SkMatrix& viewMatrix, |
1181 | const SkPath& path, |
1182 | const SkDrawShadowRec& rec) { |
1183 | ASSERT_SINGLE_OWNER |
1184 | if (fContext->abandoned()) { |
1185 | return true; |
1186 | } |
1187 | SkDEBUGCODE(this->validate();) |
1188 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawFastShadow" , fContext); |
1189 | |
1190 | // check z plane |
1191 | bool tiltZPlane = SkToBool(!SkScalarNearlyZero(rec.fZPlaneParams.fX) || |
1192 | !SkScalarNearlyZero(rec.fZPlaneParams.fY)); |
1193 | bool skipAnalytic = SkToBool(rec.fFlags & SkShadowFlags::kGeometricOnly_ShadowFlag); |
1194 | if (tiltZPlane || skipAnalytic || !viewMatrix.rectStaysRect() || !viewMatrix.isSimilarity()) { |
1195 | return false; |
1196 | } |
1197 | |
1198 | SkRRect rrect; |
1199 | SkRect rect; |
1200 | // we can only handle rects, circles, and rrects with circular corners |
1201 | bool isRRect = path.isRRect(&rrect) && SkRRectPriv::IsSimpleCircular(rrect) && |
1202 | rrect.radii(SkRRect::kUpperLeft_Corner).fX > SK_ScalarNearlyZero; |
1203 | if (!isRRect && |
1204 | path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height()) && |
1205 | rect.width() > SK_ScalarNearlyZero) { |
1206 | rrect.setOval(rect); |
1207 | isRRect = true; |
1208 | } |
1209 | if (!isRRect && path.isRect(&rect)) { |
1210 | rrect.setRect(rect); |
1211 | isRRect = true; |
1212 | } |
1213 | |
1214 | if (!isRRect) { |
1215 | return false; |
1216 | } |
1217 | |
1218 | if (rrect.isEmpty()) { |
1219 | return true; |
1220 | } |
1221 | |
1222 | AutoCheckFlush acf(this->drawingManager()); |
1223 | |
1224 | // transform light |
1225 | SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos); |
1226 | |
1227 | // 1/scale |
1228 | SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ? |
1229 | SkScalarInvert(SkScalarAbs(viewMatrix[SkMatrix::kMScaleX])) : |
1230 | sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] + |
1231 | viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]); |
1232 | |
1233 | SkScalar occluderHeight = rec.fZPlaneParams.fZ; |
1234 | bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag); |
1235 | |
1236 | if (SkColorGetA(rec.fAmbientColor) > 0) { |
1237 | SkScalar devSpaceInsetWidth = SkDrawShadowMetrics::AmbientBlurRadius(occluderHeight); |
1238 | const SkScalar umbraRecipAlpha = SkDrawShadowMetrics::AmbientRecipAlpha(occluderHeight); |
1239 | const SkScalar devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha; |
1240 | |
1241 | // Outset the shadow rrect to the border of the penumbra |
1242 | SkScalar ambientPathOutset = devSpaceInsetWidth * devToSrcScale; |
1243 | SkRRect ambientRRect; |
1244 | SkRect outsetRect = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset); |
1245 | // If the rrect was an oval then its outset will also be one. |
1246 | // We set it explicitly to avoid errors. |
1247 | if (rrect.isOval()) { |
1248 | ambientRRect = SkRRect::MakeOval(outsetRect); |
1249 | } else { |
1250 | SkScalar outsetRad = SkRRectPriv::GetSimpleRadii(rrect).fX + ambientPathOutset; |
1251 | ambientRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad); |
1252 | } |
1253 | |
1254 | GrColor ambientColor = SkColorToPremulGrColor(rec.fAmbientColor); |
1255 | if (transparent) { |
1256 | // set a large inset to force a fill |
1257 | devSpaceInsetWidth = ambientRRect.width(); |
1258 | } |
1259 | |
1260 | std::unique_ptr<GrDrawOp> op = GrShadowRRectOp::Make(fContext, |
1261 | ambientColor, |
1262 | viewMatrix, |
1263 | ambientRRect, |
1264 | devSpaceAmbientBlur, |
1265 | devSpaceInsetWidth); |
1266 | if (op) { |
1267 | this->addDrawOp(clip, std::move(op)); |
1268 | } |
1269 | } |
1270 | |
1271 | if (SkColorGetA(rec.fSpotColor) > 0) { |
1272 | SkScalar devSpaceSpotBlur; |
1273 | SkScalar spotScale; |
1274 | SkVector spotOffset; |
1275 | SkDrawShadowMetrics::GetSpotParams(occluderHeight, devLightPos.fX, devLightPos.fY, |
1276 | devLightPos.fZ, rec.fLightRadius, |
1277 | &devSpaceSpotBlur, &spotScale, &spotOffset); |
1278 | // handle scale of radius due to CTM |
1279 | const SkScalar srcSpaceSpotBlur = devSpaceSpotBlur * devToSrcScale; |
1280 | |
1281 | // Adjust translate for the effect of the scale. |
1282 | spotOffset.fX += spotScale*viewMatrix[SkMatrix::kMTransX]; |
1283 | spotOffset.fY += spotScale*viewMatrix[SkMatrix::kMTransY]; |
1284 | // This offset is in dev space, need to transform it into source space. |
1285 | SkMatrix ctmInverse; |
1286 | if (viewMatrix.invert(&ctmInverse)) { |
1287 | ctmInverse.mapPoints(&spotOffset, 1); |
1288 | } else { |
1289 | // Since the matrix is a similarity, this should never happen, but just in case... |
1290 | SkDebugf("Matrix is degenerate. Will not render spot shadow correctly!\n" ); |
1291 | SkASSERT(false); |
1292 | } |
1293 | |
1294 | // Compute the transformed shadow rrect |
1295 | SkRRect spotShadowRRect; |
1296 | SkMatrix shadowTransform; |
1297 | shadowTransform.setScaleTranslate(spotScale, spotScale, spotOffset.fX, spotOffset.fY); |
1298 | rrect.transform(shadowTransform, &spotShadowRRect); |
1299 | SkScalar spotRadius = SkRRectPriv::GetSimpleRadii(spotShadowRRect).fX; |
1300 | |
1301 | // Compute the insetWidth |
1302 | SkScalar blurOutset = srcSpaceSpotBlur; |
1303 | SkScalar insetWidth = blurOutset; |
1304 | if (transparent) { |
1305 | // If transparent, just do a fill |
1306 | insetWidth += spotShadowRRect.width(); |
1307 | } else { |
1308 | // For shadows, instead of using a stroke we specify an inset from the penumbra |
1309 | // border. We want to extend this inset area so that it meets up with the caster |
1310 | // geometry. The inset geometry will by default already be inset by the blur width. |
1311 | // |
1312 | // We compare the min and max corners inset by the radius between the original |
1313 | // rrect and the shadow rrect. The distance between the two plus the difference |
1314 | // between the scaled radius and the original radius gives the distance from the |
1315 | // transformed shadow shape to the original shape in that corner. The max |
1316 | // of these gives the maximum distance we need to cover. |
1317 | // |
1318 | // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to |
1319 | // that to get the full insetWidth. |
1320 | SkScalar maxOffset; |
1321 | if (rrect.isRect()) { |
1322 | // Manhattan distance works better for rects |
1323 | maxOffset = std::max(std::max(SkTAbs(spotShadowRRect.rect().fLeft - |
1324 | rrect.rect().fLeft), |
1325 | SkTAbs(spotShadowRRect.rect().fTop - |
1326 | rrect.rect().fTop)), |
1327 | std::max(SkTAbs(spotShadowRRect.rect().fRight - |
1328 | rrect.rect().fRight), |
1329 | SkTAbs(spotShadowRRect.rect().fBottom - |
1330 | rrect.rect().fBottom))); |
1331 | } else { |
1332 | SkScalar dr = spotRadius - SkRRectPriv::GetSimpleRadii(rrect).fX; |
1333 | SkPoint upperLeftOffset = SkPoint::Make(spotShadowRRect.rect().fLeft - |
1334 | rrect.rect().fLeft + dr, |
1335 | spotShadowRRect.rect().fTop - |
1336 | rrect.rect().fTop + dr); |
1337 | SkPoint lowerRightOffset = SkPoint::Make(spotShadowRRect.rect().fRight - |
1338 | rrect.rect().fRight - dr, |
1339 | spotShadowRRect.rect().fBottom - |
1340 | rrect.rect().fBottom - dr); |
1341 | maxOffset = SkScalarSqrt(std::max(SkPointPriv::LengthSqd(upperLeftOffset), |
1342 | SkPointPriv::LengthSqd(lowerRightOffset))) + dr; |
1343 | } |
1344 | insetWidth += std::max(blurOutset, maxOffset); |
1345 | } |
1346 | |
1347 | // Outset the shadow rrect to the border of the penumbra |
1348 | SkRect outsetRect = spotShadowRRect.rect().makeOutset(blurOutset, blurOutset); |
1349 | if (spotShadowRRect.isOval()) { |
1350 | spotShadowRRect = SkRRect::MakeOval(outsetRect); |
1351 | } else { |
1352 | SkScalar outsetRad = spotRadius + blurOutset; |
1353 | spotShadowRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad); |
1354 | } |
1355 | |
1356 | GrColor spotColor = SkColorToPremulGrColor(rec.fSpotColor); |
1357 | |
1358 | std::unique_ptr<GrDrawOp> op = GrShadowRRectOp::Make(fContext, |
1359 | spotColor, |
1360 | viewMatrix, |
1361 | spotShadowRRect, |
1362 | 2.0f * devSpaceSpotBlur, |
1363 | insetWidth); |
1364 | if (op) { |
1365 | this->addDrawOp(clip, std::move(op)); |
1366 | } |
1367 | } |
1368 | |
1369 | return true; |
1370 | } |
1371 | |
1372 | /////////////////////////////////////////////////////////////////////////////// |
1373 | |
1374 | bool GrRenderTargetContext::drawFilledDRRect(const GrClip* clip, |
1375 | GrPaint&& paint, |
1376 | GrAA aa, |
1377 | const SkMatrix& viewMatrix, |
1378 | const SkRRect& origOuter, |
1379 | const SkRRect& origInner) { |
1380 | SkASSERT(!origInner.isEmpty()); |
1381 | SkASSERT(!origOuter.isEmpty()); |
1382 | |
1383 | SkTCopyOnFirstWrite<SkRRect> inner(origInner), outer(origOuter); |
1384 | |
1385 | GrAAType aaType = this->chooseAAType(aa); |
1386 | |
1387 | if (GrAAType::kMSAA == aaType) { |
1388 | return false; |
1389 | } |
1390 | |
1391 | if (GrAAType::kCoverage == aaType && SkRRectPriv::IsCircle(*inner) |
1392 | && SkRRectPriv::IsCircle(*outer)) { |
1393 | auto outerR = outer->width() / 2.f; |
1394 | auto innerR = inner->width() / 2.f; |
1395 | auto cx = outer->getBounds().fLeft + outerR; |
1396 | auto cy = outer->getBounds().fTop + outerR; |
1397 | if (SkScalarNearlyEqual(cx, inner->getBounds().fLeft + innerR) && |
1398 | SkScalarNearlyEqual(cy, inner->getBounds().fTop + innerR)) { |
1399 | auto avgR = (innerR + outerR) / 2.f; |
1400 | auto circleBounds = SkRect::MakeLTRB(cx - avgR, cy - avgR, cx + avgR, cy + avgR); |
1401 | SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
1402 | stroke.setStrokeStyle(outerR - innerR); |
1403 | auto op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), viewMatrix, |
1404 | circleBounds, GrStyle(stroke, nullptr), |
1405 | this->caps()->shaderCaps()); |
1406 | if (op) { |
1407 | this->addDrawOp(clip, std::move(op)); |
1408 | return true; |
1409 | } |
1410 | assert_alive(paint); |
1411 | } |
1412 | } |
1413 | |
1414 | GrClipEdgeType innerEdgeType, outerEdgeType; |
1415 | if (GrAAType::kCoverage == aaType) { |
1416 | innerEdgeType = GrClipEdgeType::kInverseFillAA; |
1417 | outerEdgeType = GrClipEdgeType::kFillAA; |
1418 | } else { |
1419 | innerEdgeType = GrClipEdgeType::kInverseFillBW; |
1420 | outerEdgeType = GrClipEdgeType::kFillBW; |
1421 | } |
1422 | |
1423 | SkMatrix inverseVM; |
1424 | if (!viewMatrix.isIdentity()) { |
1425 | if (!origInner.transform(viewMatrix, inner.writable())) { |
1426 | return false; |
1427 | } |
1428 | if (!origOuter.transform(viewMatrix, outer.writable())) { |
1429 | return false; |
1430 | } |
1431 | if (!viewMatrix.invert(&inverseVM)) { |
1432 | return false; |
1433 | } |
1434 | } else { |
1435 | inverseVM.reset(); |
1436 | } |
1437 | |
1438 | const auto& caps = *this->caps()->shaderCaps(); |
1439 | // TODO these need to be a geometry processors |
1440 | auto [success, fp] = GrRRectEffect::Make(/*inputFP=*/nullptr, innerEdgeType, *inner, caps); |
1441 | if (!success) { |
1442 | return false; |
1443 | } |
1444 | |
1445 | std::tie(success, fp) = GrRRectEffect::Make(std::move(fp), outerEdgeType, *outer, caps); |
1446 | if (!success) { |
1447 | return false; |
1448 | } |
1449 | |
1450 | paint.setCoverageFragmentProcessor(std::move(fp)); |
1451 | |
1452 | SkRect bounds = outer->getBounds(); |
1453 | if (GrAAType::kCoverage == aaType) { |
1454 | bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
1455 | } |
1456 | |
1457 | this->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), bounds, |
1458 | inverseVM); |
1459 | return true; |
1460 | } |
1461 | |
1462 | void GrRenderTargetContext::drawDRRect(const GrClip* clip, |
1463 | GrPaint&& paint, |
1464 | GrAA aa, |
1465 | const SkMatrix& viewMatrix, |
1466 | const SkRRect& outer, |
1467 | const SkRRect& inner) { |
1468 | ASSERT_SINGLE_OWNER |
1469 | RETURN_IF_ABANDONED |
1470 | SkDEBUGCODE(this->validate();) |
1471 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawDRRect" , fContext); |
1472 | |
1473 | SkASSERT(!outer.isEmpty()); |
1474 | SkASSERT(!inner.isEmpty()); |
1475 | |
1476 | AutoCheckFlush acf(this->drawingManager()); |
1477 | |
1478 | if (this->drawFilledDRRect(clip, std::move(paint), aa, viewMatrix, outer, inner)) { |
1479 | return; |
1480 | } |
1481 | assert_alive(paint); |
1482 | |
1483 | SkPath path; |
1484 | path.setIsVolatile(true); |
1485 | path.addRRect(inner); |
1486 | path.addRRect(outer); |
1487 | path.setFillType(SkPathFillType::kEvenOdd); |
1488 | this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrStyledShape(path)); |
1489 | } |
1490 | |
1491 | /////////////////////////////////////////////////////////////////////////////// |
1492 | |
1493 | void GrRenderTargetContext::drawRegion(const GrClip* clip, |
1494 | GrPaint&& paint, |
1495 | GrAA aa, |
1496 | const SkMatrix& viewMatrix, |
1497 | const SkRegion& region, |
1498 | const GrStyle& style, |
1499 | const GrUserStencilSettings* ss) { |
1500 | ASSERT_SINGLE_OWNER |
1501 | RETURN_IF_ABANDONED |
1502 | SkDEBUGCODE(this->validate();) |
1503 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawRegion" , fContext); |
1504 | |
1505 | if (GrAA::kYes == aa) { |
1506 | // GrRegionOp performs no antialiasing but is much faster, so here we check the matrix |
1507 | // to see whether aa is really required. |
1508 | if (!SkToBool(viewMatrix.getType() & ~(SkMatrix::kTranslate_Mask)) && |
1509 | SkScalarIsInt(viewMatrix.getTranslateX()) && |
1510 | SkScalarIsInt(viewMatrix.getTranslateY())) { |
1511 | aa = GrAA::kNo; |
1512 | } |
1513 | } |
1514 | bool complexStyle = !style.isSimpleFill(); |
1515 | if (complexStyle || GrAA::kYes == aa) { |
1516 | SkPath path; |
1517 | region.getBoundaryPath(&path); |
1518 | path.setIsVolatile(true); |
1519 | |
1520 | return this->drawPath(clip, std::move(paint), aa, viewMatrix, path, style); |
1521 | } |
1522 | |
1523 | GrAAType aaType = this->chooseAAType(GrAA::kNo); |
1524 | std::unique_ptr<GrDrawOp> op = GrRegionOp::Make(fContext, std::move(paint), viewMatrix, region, |
1525 | aaType, ss); |
1526 | this->addDrawOp(clip, std::move(op)); |
1527 | } |
1528 | |
1529 | void GrRenderTargetContext::drawOval(const GrClip* clip, |
1530 | GrPaint&& paint, |
1531 | GrAA aa, |
1532 | const SkMatrix& viewMatrix, |
1533 | const SkRect& oval, |
1534 | const GrStyle& style) { |
1535 | ASSERT_SINGLE_OWNER |
1536 | RETURN_IF_ABANDONED |
1537 | SkDEBUGCODE(this->validate();) |
1538 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawOval" , fContext); |
1539 | |
1540 | const SkStrokeRec& stroke = style.strokeRec(); |
1541 | |
1542 | if (oval.isEmpty() && !style.pathEffect()) { |
1543 | if (stroke.getStyle() == SkStrokeRec::kFill_Style) { |
1544 | return; |
1545 | } |
1546 | |
1547 | this->drawRect(clip, std::move(paint), aa, viewMatrix, oval, &style); |
1548 | return; |
1549 | } |
1550 | |
1551 | AutoCheckFlush acf(this->drawingManager()); |
1552 | |
1553 | GrAAType aaType = this->chooseAAType(aa); |
1554 | |
1555 | std::unique_ptr<GrDrawOp> op; |
1556 | if (GrAAType::kCoverage == aaType && oval.width() > SK_ScalarNearlyZero && |
1557 | oval.width() == oval.height() && viewMatrix.isSimilarity()) { |
1558 | // We don't draw true circles as round rects in coverage mode, because it can |
1559 | // cause perf regressions on some platforms as compared to the dedicated circle Op. |
1560 | assert_alive(paint); |
1561 | op = GrOvalOpFactory::MakeCircleOp(fContext, std::move(paint), viewMatrix, oval, style, |
1562 | this->caps()->shaderCaps()); |
1563 | } |
1564 | if (!op && style.isSimpleFill()) { |
1565 | // GrFillRRectOp has special geometry and a fragment-shader branch to conditionally evaluate |
1566 | // the arc equation. This same special geometry and fragment branch also turn out to be a |
1567 | // substantial optimization for drawing ovals (namely, by not evaluating the arc equation |
1568 | // inside the oval's inner diamond). Given these optimizations, it's a clear win to draw |
1569 | // ovals the exact same way we do round rects. |
1570 | assert_alive(paint); |
1571 | op = GrFillRRectOp::Make(fContext, std::move(paint), viewMatrix, SkRRect::MakeOval(oval), |
1572 | aaType); |
1573 | } |
1574 | if (!op && GrAAType::kCoverage == aaType) { |
1575 | assert_alive(paint); |
1576 | op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), viewMatrix, oval, style, |
1577 | this->caps()->shaderCaps()); |
1578 | } |
1579 | if (op) { |
1580 | this->addDrawOp(clip, std::move(op)); |
1581 | return; |
1582 | } |
1583 | |
1584 | assert_alive(paint); |
1585 | this->drawShapeUsingPathRenderer( |
1586 | clip, std::move(paint), aa, viewMatrix, |
1587 | GrStyledShape(SkRRect::MakeOval(oval), SkPathDirection::kCW, 2, false, style)); |
1588 | } |
1589 | |
1590 | void GrRenderTargetContext::drawArc(const GrClip* clip, |
1591 | GrPaint&& paint, |
1592 | GrAA aa, |
1593 | const SkMatrix& viewMatrix, |
1594 | const SkRect& oval, |
1595 | SkScalar startAngle, |
1596 | SkScalar sweepAngle, |
1597 | bool useCenter, |
1598 | const GrStyle& style) { |
1599 | ASSERT_SINGLE_OWNER |
1600 | RETURN_IF_ABANDONED |
1601 | SkDEBUGCODE(this->validate();) |
1602 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawArc" , fContext); |
1603 | |
1604 | AutoCheckFlush acf(this->drawingManager()); |
1605 | |
1606 | GrAAType aaType = this->chooseAAType(aa); |
1607 | if (GrAAType::kCoverage == aaType) { |
1608 | const GrShaderCaps* shaderCaps = this->caps()->shaderCaps(); |
1609 | std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeArcOp(fContext, |
1610 | std::move(paint), |
1611 | viewMatrix, |
1612 | oval, |
1613 | startAngle, |
1614 | sweepAngle, |
1615 | useCenter, |
1616 | style, |
1617 | shaderCaps); |
1618 | if (op) { |
1619 | this->addDrawOp(clip, std::move(op)); |
1620 | return; |
1621 | } |
1622 | assert_alive(paint); |
1623 | } |
1624 | this->drawShapeUsingPathRenderer( |
1625 | clip, std::move(paint), aa, viewMatrix, |
1626 | GrStyledShape::MakeArc(oval, startAngle, sweepAngle, useCenter, style)); |
1627 | } |
1628 | |
1629 | void GrRenderTargetContext::drawImageLattice(const GrClip* clip, |
1630 | GrPaint&& paint, |
1631 | const SkMatrix& viewMatrix, |
1632 | GrSurfaceProxyView view, |
1633 | SkAlphaType alphaType, |
1634 | sk_sp<GrColorSpaceXform> csxf, |
1635 | GrSamplerState::Filter filter, |
1636 | std::unique_ptr<SkLatticeIter> iter, |
1637 | const SkRect& dst) { |
1638 | ASSERT_SINGLE_OWNER |
1639 | RETURN_IF_ABANDONED |
1640 | SkDEBUGCODE(this->validate();) |
1641 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawImageLattice" , fContext); |
1642 | |
1643 | AutoCheckFlush acf(this->drawingManager()); |
1644 | |
1645 | std::unique_ptr<GrDrawOp> op = |
1646 | GrLatticeOp::MakeNonAA(fContext, std::move(paint), viewMatrix, std::move(view), |
1647 | alphaType, std::move(csxf), filter, std::move(iter), dst); |
1648 | this->addDrawOp(clip, std::move(op)); |
1649 | } |
1650 | |
1651 | void GrRenderTargetContext::drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable, |
1652 | const SkRect& bounds) { |
1653 | std::unique_ptr<GrOp> op(GrDrawableOp::Make(fContext, std::move(drawable), bounds)); |
1654 | SkASSERT(op); |
1655 | this->addOp(std::move(op)); |
1656 | } |
1657 | |
1658 | bool GrRenderTargetContext::waitOnSemaphores(int numSemaphores, |
1659 | const GrBackendSemaphore waitSemaphores[], |
1660 | bool deleteSemaphoresAfterWait) { |
1661 | ASSERT_SINGLE_OWNER |
1662 | RETURN_FALSE_IF_ABANDONED |
1663 | SkDEBUGCODE(this->validate();) |
1664 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "waitOnSemaphores" , fContext); |
1665 | |
1666 | AutoCheckFlush acf(this->drawingManager()); |
1667 | |
1668 | if (numSemaphores && !this->caps()->semaphoreSupport()) { |
1669 | return false; |
1670 | } |
1671 | |
1672 | auto direct = fContext->asDirectContext(); |
1673 | if (!direct) { |
1674 | return false; |
1675 | } |
1676 | |
1677 | auto resourceProvider = direct->priv().resourceProvider(); |
1678 | |
1679 | GrWrapOwnership ownership = |
1680 | deleteSemaphoresAfterWait ? kAdopt_GrWrapOwnership : kBorrow_GrWrapOwnership; |
1681 | |
1682 | std::unique_ptr<std::unique_ptr<GrSemaphore>[]> grSemaphores( |
1683 | new std::unique_ptr<GrSemaphore>[numSemaphores]); |
1684 | for (int i = 0; i < numSemaphores; ++i) { |
1685 | grSemaphores[i] = resourceProvider->wrapBackendSemaphore( |
1686 | waitSemaphores[i], GrResourceProvider::SemaphoreWrapType::kWillWait, ownership); |
1687 | } |
1688 | this->drawingManager()->newWaitRenderTask(this->asSurfaceProxyRef(), std::move(grSemaphores), |
1689 | numSemaphores); |
1690 | return true; |
1691 | } |
1692 | |
1693 | void GrRenderTargetContext::drawPath(const GrClip* clip, |
1694 | GrPaint&& paint, |
1695 | GrAA aa, |
1696 | const SkMatrix& viewMatrix, |
1697 | const SkPath& path, |
1698 | const GrStyle& style) { |
1699 | ASSERT_SINGLE_OWNER |
1700 | RETURN_IF_ABANDONED |
1701 | SkDEBUGCODE(this->validate();) |
1702 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawPath" , fContext); |
1703 | |
1704 | GrStyledShape shape(path, style); |
1705 | |
1706 | this->drawShape(clip, std::move(paint), aa, viewMatrix, shape); |
1707 | } |
1708 | |
1709 | void GrRenderTargetContext::drawShape(const GrClip* clip, |
1710 | GrPaint&& paint, |
1711 | GrAA aa, |
1712 | const SkMatrix& viewMatrix, |
1713 | const GrStyledShape& shape) { |
1714 | ASSERT_SINGLE_OWNER |
1715 | RETURN_IF_ABANDONED |
1716 | SkDEBUGCODE(this->validate();) |
1717 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "drawShape" , fContext); |
1718 | |
1719 | if (shape.isEmpty()) { |
1720 | if (shape.inverseFilled()) { |
1721 | this->drawPaint(clip, std::move(paint), viewMatrix); |
1722 | } |
1723 | return; |
1724 | } |
1725 | |
1726 | AutoCheckFlush acf(this->drawingManager()); |
1727 | |
1728 | if (!shape.style().hasPathEffect()) { |
1729 | GrAAType aaType = this->chooseAAType(aa); |
1730 | SkRRect rrect; |
1731 | // We can ignore the starting point and direction since there is no path effect. |
1732 | bool inverted; |
1733 | if (shape.asRRect(&rrect, nullptr, nullptr, &inverted) && !inverted) { |
1734 | if (rrect.isRect()) { |
1735 | this->drawRect(clip, std::move(paint), aa, viewMatrix, rrect.rect(), |
1736 | &shape.style()); |
1737 | return; |
1738 | } else if (rrect.isOval()) { |
1739 | this->drawOval(clip, std::move(paint), aa, viewMatrix, rrect.rect(), shape.style()); |
1740 | return; |
1741 | } |
1742 | this->drawRRect(clip, std::move(paint), aa, viewMatrix, rrect, shape.style()); |
1743 | return; |
1744 | } else if (GrAAType::kCoverage == aaType && shape.style().isSimpleFill() && |
1745 | viewMatrix.rectStaysRect()) { |
1746 | // TODO: the rectStaysRect restriction could be lifted if we were willing to apply |
1747 | // the matrix to all the points individually rather than just to the rect |
1748 | SkRect rects[2]; |
1749 | if (shape.asNestedRects(rects)) { |
1750 | // Concave AA paths are expensive - try to avoid them for special cases |
1751 | std::unique_ptr<GrDrawOp> op = GrStrokeRectOp::MakeNested( |
1752 | fContext, std::move(paint), viewMatrix, rects); |
1753 | if (op) { |
1754 | this->addDrawOp(clip, std::move(op)); |
1755 | } |
1756 | // Returning here indicates that there is nothing to draw in this case. |
1757 | return; |
1758 | } |
1759 | } |
1760 | } |
1761 | |
1762 | // If we get here in drawShape(), we definitely need to use path rendering |
1763 | this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, shape, |
1764 | /* attempt fallback */ false); |
1765 | } |
1766 | |
1767 | static SkIRect get_clip_bounds(const GrRenderTargetContext* rtc, const GrClip* clip) { |
1768 | return clip ? clip->getConservativeBounds() : SkIRect::MakeWH(rtc->width(), rtc->height()); |
1769 | } |
1770 | |
1771 | bool GrRenderTargetContextPriv::drawAndStencilPath(const GrHardClip* clip, |
1772 | const GrUserStencilSettings* ss, |
1773 | SkRegion::Op op, |
1774 | bool invert, |
1775 | GrAA aa, |
1776 | const SkMatrix& viewMatrix, |
1777 | const SkPath& path) { |
1778 | ASSERT_SINGLE_OWNER_PRIV |
1779 | RETURN_FALSE_IF_ABANDONED_PRIV |
1780 | SkDEBUGCODE(fRenderTargetContext->validate();) |
1781 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv" , "drawAndStencilPath" , |
1782 | fRenderTargetContext->fContext); |
1783 | |
1784 | if (path.isEmpty() && path.isInverseFillType()) { |
1785 | GrPaint paint; |
1786 | paint.setCoverageSetOpXPFactory(op, invert); |
1787 | this->stencilRect(clip, ss, std::move(paint), GrAA::kNo, SkMatrix::I(), |
1788 | SkRect::MakeIWH(fRenderTargetContext->width(), |
1789 | fRenderTargetContext->height())); |
1790 | return true; |
1791 | } |
1792 | |
1793 | AutoCheckFlush acf(fRenderTargetContext->drawingManager()); |
1794 | |
1795 | // An Assumption here is that path renderer would use some form of tweaking |
1796 | // the src color (either the input alpha or in the frag shader) to implement |
1797 | // aa. If we have some future driver-mojo path AA that can do the right |
1798 | // thing WRT to the blend then we'll need some query on the PR. |
1799 | GrAAType aaType = fRenderTargetContext->chooseAAType(aa); |
1800 | bool hasUserStencilSettings = !ss->isUnused(); |
1801 | |
1802 | SkIRect clipConservativeBounds = get_clip_bounds(fRenderTargetContext, clip); |
1803 | |
1804 | GrPaint paint; |
1805 | paint.setCoverageSetOpXPFactory(op, invert); |
1806 | |
1807 | GrStyledShape shape(path, GrStyle::SimpleFill()); |
1808 | GrPathRenderer::CanDrawPathArgs canDrawArgs; |
1809 | canDrawArgs.fCaps = fRenderTargetContext->caps(); |
1810 | canDrawArgs.fProxy = fRenderTargetContext->asRenderTargetProxy(); |
1811 | canDrawArgs.fViewMatrix = &viewMatrix; |
1812 | canDrawArgs.fShape = &shape; |
1813 | canDrawArgs.fPaint = &paint; |
1814 | canDrawArgs.fClipConservativeBounds = &clipConservativeBounds; |
1815 | canDrawArgs.fAAType = aaType; |
1816 | SkASSERT(!fRenderTargetContext->wrapsVkSecondaryCB()); |
1817 | canDrawArgs.fTargetIsWrappedVkSecondaryCB = false; |
1818 | canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings; |
1819 | |
1820 | // Don't allow the SW renderer |
1821 | GrPathRenderer* pr = fRenderTargetContext->drawingManager()->getPathRenderer( |
1822 | canDrawArgs, false, GrPathRendererChain::DrawType::kStencilAndColor); |
1823 | if (!pr) { |
1824 | return false; |
1825 | } |
1826 | |
1827 | GrPathRenderer::DrawPathArgs args{fRenderTargetContext->drawingManager()->getContext(), |
1828 | std::move(paint), |
1829 | ss, |
1830 | fRenderTargetContext, |
1831 | clip, |
1832 | &clipConservativeBounds, |
1833 | &viewMatrix, |
1834 | &shape, |
1835 | aaType, |
1836 | fRenderTargetContext->colorInfo().isLinearlyBlended()}; |
1837 | pr->drawPath(args); |
1838 | return true; |
1839 | } |
1840 | |
1841 | SkBudgeted GrRenderTargetContextPriv::isBudgeted() const { |
1842 | ASSERT_SINGLE_OWNER_PRIV |
1843 | |
1844 | if (fRenderTargetContext->fContext->abandoned()) { |
1845 | return SkBudgeted::kNo; |
1846 | } |
1847 | |
1848 | SkDEBUGCODE(fRenderTargetContext->validate();) |
1849 | |
1850 | return fRenderTargetContext->asSurfaceProxy()->isBudgeted(); |
1851 | } |
1852 | |
1853 | void GrRenderTargetContext::drawShapeUsingPathRenderer(const GrClip* clip, |
1854 | GrPaint&& paint, |
1855 | GrAA aa, |
1856 | const SkMatrix& viewMatrix, |
1857 | const GrStyledShape& originalShape, |
1858 | bool attemptShapeFallback) { |
1859 | ASSERT_SINGLE_OWNER |
1860 | RETURN_IF_ABANDONED |
1861 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "internalDrawPath" , fContext); |
1862 | |
1863 | if (!viewMatrix.isFinite() || !originalShape.bounds().isFinite()) { |
1864 | return; |
1865 | } |
1866 | |
1867 | if (attemptShapeFallback && originalShape.simplified()) { |
1868 | // Usually we enter drawShapeUsingPathRenderer() because the shape+style was too |
1869 | // complex for dedicated draw ops. However, if GrStyledShape was able to reduce something |
1870 | // we ought to try again instead of going right to path rendering. |
1871 | this->drawShape(clip, std::move(paint), aa, viewMatrix, originalShape); |
1872 | return; |
1873 | } |
1874 | |
1875 | SkIRect clipConservativeBounds = get_clip_bounds(this, clip); |
1876 | |
1877 | GrStyledShape tempShape; |
1878 | GrAAType aaType = this->chooseAAType(aa); |
1879 | |
1880 | GrPathRenderer::CanDrawPathArgs canDrawArgs; |
1881 | canDrawArgs.fCaps = this->caps(); |
1882 | canDrawArgs.fProxy = this->asRenderTargetProxy(); |
1883 | canDrawArgs.fViewMatrix = &viewMatrix; |
1884 | canDrawArgs.fShape = &originalShape; |
1885 | canDrawArgs.fPaint = &paint; |
1886 | canDrawArgs.fClipConservativeBounds = &clipConservativeBounds; |
1887 | canDrawArgs.fTargetIsWrappedVkSecondaryCB = this->wrapsVkSecondaryCB(); |
1888 | canDrawArgs.fHasUserStencilSettings = false; |
1889 | |
1890 | GrPathRenderer* pr; |
1891 | static constexpr GrPathRendererChain::DrawType kType = GrPathRendererChain::DrawType::kColor; |
1892 | if (originalShape.isEmpty() && !originalShape.inverseFilled()) { |
1893 | return; |
1894 | } |
1895 | |
1896 | canDrawArgs.fAAType = aaType; |
1897 | |
1898 | // Try a 1st time without applying any of the style to the geometry (and barring sw) |
1899 | pr = this->drawingManager()->getPathRenderer(canDrawArgs, false, kType); |
1900 | SkScalar styleScale = GrStyle::MatrixToScaleFactor(viewMatrix); |
1901 | |
1902 | if (!pr && originalShape.style().pathEffect()) { |
1903 | // It didn't work above, so try again with the path effect applied. |
1904 | tempShape = originalShape.applyStyle(GrStyle::Apply::kPathEffectOnly, styleScale); |
1905 | if (tempShape.isEmpty()) { |
1906 | return; |
1907 | } |
1908 | canDrawArgs.fShape = &tempShape; |
1909 | pr = this->drawingManager()->getPathRenderer(canDrawArgs, false, kType); |
1910 | } |
1911 | if (!pr) { |
1912 | if (canDrawArgs.fShape->style().applies()) { |
1913 | tempShape = canDrawArgs.fShape->applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, |
1914 | styleScale); |
1915 | if (tempShape.isEmpty()) { |
1916 | return; |
1917 | } |
1918 | canDrawArgs.fShape = &tempShape; |
1919 | // This time, allow SW renderer |
1920 | pr = this->drawingManager()->getPathRenderer(canDrawArgs, true, kType); |
1921 | } else { |
1922 | pr = this->drawingManager()->getSoftwarePathRenderer(); |
1923 | #if GR_PATH_RENDERER_SPEW |
1924 | SkDebugf("falling back to: %s\n" , pr->name()); |
1925 | #endif |
1926 | } |
1927 | } |
1928 | |
1929 | if (!pr) { |
1930 | #ifdef SK_DEBUG |
1931 | SkDebugf("Unable to find path renderer compatible with path.\n" ); |
1932 | #endif |
1933 | return; |
1934 | } |
1935 | |
1936 | GrPathRenderer::DrawPathArgs args{this->drawingManager()->getContext(), |
1937 | std::move(paint), |
1938 | &GrUserStencilSettings::kUnused, |
1939 | this, |
1940 | clip, |
1941 | &clipConservativeBounds, |
1942 | &viewMatrix, |
1943 | canDrawArgs.fShape, |
1944 | aaType, |
1945 | this->colorInfo().isLinearlyBlended()}; |
1946 | pr->drawPath(args); |
1947 | } |
1948 | |
1949 | static void op_bounds(SkRect* bounds, const GrOp* op) { |
1950 | *bounds = op->bounds(); |
1951 | if (op->hasZeroArea()) { |
1952 | if (op->hasAABloat()) { |
1953 | bounds->outset(0.5f, 0.5f); |
1954 | } else { |
1955 | // We don't know which way the particular GPU will snap lines or points at integer |
1956 | // coords. So we ensure that the bounds is large enough for either snap. |
1957 | SkRect before = *bounds; |
1958 | bounds->roundOut(bounds); |
1959 | if (bounds->fLeft == before.fLeft) { |
1960 | bounds->fLeft -= 1; |
1961 | } |
1962 | if (bounds->fTop == before.fTop) { |
1963 | bounds->fTop -= 1; |
1964 | } |
1965 | if (bounds->fRight == before.fRight) { |
1966 | bounds->fRight += 1; |
1967 | } |
1968 | if (bounds->fBottom == before.fBottom) { |
1969 | bounds->fBottom += 1; |
1970 | } |
1971 | } |
1972 | } |
1973 | } |
1974 | |
1975 | void GrRenderTargetContext::addOp(std::unique_ptr<GrOp> op) { |
1976 | GrDrawingManager* drawingMgr = this->drawingManager(); |
1977 | this->getOpsTask()->addOp(drawingMgr, |
1978 | std::move(op), GrTextureResolveManager(drawingMgr), *this->caps()); |
1979 | } |
1980 | |
1981 | void GrRenderTargetContext::addDrawOp(const GrClip* clip, std::unique_ptr<GrDrawOp> op, |
1982 | const std::function<WillAddOpFn>& willAddFn) { |
1983 | ASSERT_SINGLE_OWNER |
1984 | if (fContext->abandoned()) { |
1985 | fContext->priv().opMemoryPool()->release(std::move(op)); |
1986 | return; |
1987 | } |
1988 | SkDEBUGCODE(this->validate();) |
1989 | SkDEBUGCODE(op->fAddDrawOpCalled = true;) |
1990 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "addDrawOp" , fContext); |
1991 | |
1992 | // Setup clip |
1993 | SkRect bounds; |
1994 | op_bounds(&bounds, op.get()); |
1995 | GrAppliedClip appliedClip(this->dimensions(), this->asSurfaceProxy()->backingStoreDimensions()); |
1996 | GrDrawOp::FixedFunctionFlags fixedFunctionFlags = op->fixedFunctionFlags(); |
1997 | bool usesHWAA = fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesHWAA; |
1998 | bool usesUserStencilBits = fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil; |
1999 | |
2000 | if (usesUserStencilBits) { // Stencil clipping will call setNeedsStencil on its own, if needed. |
2001 | this->setNeedsStencil(usesHWAA); |
2002 | } |
2003 | |
2004 | bool skipDraw = false; |
2005 | if (clip) { |
2006 | // Have a complex clip, so defer to its early clip culling |
2007 | GrAAType aaType = usesHWAA ? GrAAType::kMSAA : |
2008 | (op->hasAABloat() ? GrAAType::kCoverage : |
2009 | GrAAType::kNone); |
2010 | skipDraw = clip->apply(fContext, this, aaType, usesUserStencilBits, |
2011 | &appliedClip, &bounds) == GrClip::Effect::kClippedOut; |
2012 | } else { |
2013 | // No clipping, so just clip the bounds against the logical render target dimensions |
2014 | skipDraw = !bounds.intersect(this->asSurfaceProxy()->getBoundsRect()); |
2015 | } |
2016 | |
2017 | if (skipDraw) { |
2018 | fContext->priv().opMemoryPool()->release(std::move(op)); |
2019 | return; |
2020 | } |
2021 | |
2022 | bool willUseStencil = usesUserStencilBits || appliedClip.hasStencilClip(); |
2023 | SkASSERT(!willUseStencil || fNumStencilSamples > 0); |
2024 | |
2025 | // If stencil is enabled and the framebuffer is mixed sampled, then the graphics pipeline will |
2026 | // have mixed sampled coverage, regardless of whether HWAA is enabled. (e.g., a non-aa draw |
2027 | // that uses a stencil test when the stencil buffer is multisampled.) |
2028 | bool hasMixedSampledCoverage = ( |
2029 | willUseStencil && fNumStencilSamples > this->numSamples()); |
2030 | SkASSERT(!hasMixedSampledCoverage || |
2031 | this->asRenderTargetProxy()->canUseMixedSamples(*this->caps())); |
2032 | |
2033 | GrClampType clampType = GrColorTypeClampType(this->colorInfo().colorType()); |
2034 | GrProcessorSet::Analysis analysis = op->finalize( |
2035 | *this->caps(), &appliedClip, hasMixedSampledCoverage, clampType); |
2036 | |
2037 | // Must be called before setDstProxyView so that it sees the final bounds of the op. |
2038 | op->setClippedBounds(bounds); |
2039 | |
2040 | GrXferProcessor::DstProxyView dstProxyView; |
2041 | if (analysis.requiresDstTexture()) { |
2042 | if (!this->setupDstProxyView(*op, &dstProxyView)) { |
2043 | fContext->priv().opMemoryPool()->release(std::move(op)); |
2044 | return; |
2045 | } |
2046 | } |
2047 | |
2048 | auto opsTask = this->getOpsTask(); |
2049 | if (willAddFn) { |
2050 | willAddFn(op.get(), opsTask->uniqueID()); |
2051 | } |
2052 | opsTask->addDrawOp(this->drawingManager(), std::move(op), analysis, std::move(appliedClip), |
2053 | dstProxyView,GrTextureResolveManager(this->drawingManager()), *this->caps()); |
2054 | } |
2055 | |
2056 | bool GrRenderTargetContext::setupDstProxyView(const GrOp& op, |
2057 | GrXferProcessor::DstProxyView* dstProxyView) { |
2058 | // If we are wrapping a vulkan secondary command buffer, we can't make a dst copy because we |
2059 | // don't actually have a VkImage to make a copy of. Additionally we don't have the power to |
2060 | // start and stop the render pass in order to make the copy. |
2061 | if (this->asRenderTargetProxy()->wrapsVkSecondaryCB()) { |
2062 | return false; |
2063 | } |
2064 | |
2065 | if (this->caps()->textureBarrierSupport() && |
2066 | !this->asSurfaceProxy()->requiresManualMSAAResolve()) { |
2067 | if (this->asTextureProxy()) { |
2068 | // The render target is a texture, so we can read from it directly in the shader. The XP |
2069 | // will be responsible to detect this situation and request a texture barrier. |
2070 | dstProxyView->setProxyView(this->readSurfaceView()); |
2071 | dstProxyView->setOffset(0, 0); |
2072 | return true; |
2073 | } |
2074 | } |
2075 | |
2076 | GrColorType colorType = this->colorInfo().colorType(); |
2077 | // MSAA consideration: When there is support for reading MSAA samples in the shader we could |
2078 | // have per-sample dst values by making the copy multisampled. |
2079 | GrCaps::DstCopyRestrictions restrictions = this->caps()->getDstCopyRestrictions( |
2080 | this->asRenderTargetProxy(), colorType); |
2081 | |
2082 | SkIRect copyRect = SkIRect::MakeSize(this->asSurfaceProxy()->backingStoreDimensions()); |
2083 | if (!restrictions.fMustCopyWholeSrc) { |
2084 | // If we don't need the whole source, restrict to the op's bounds. We add an extra pixel |
2085 | // of padding to account for AA bloat and the unpredictable rounding of coords near pixel |
2086 | // centers during rasterization. |
2087 | SkIRect conservativeDrawBounds = op.bounds().roundOut(); |
2088 | conservativeDrawBounds.outset(1, 1); |
2089 | SkAssertResult(copyRect.intersect(conservativeDrawBounds)); |
2090 | } |
2091 | |
2092 | SkIPoint dstOffset; |
2093 | SkBackingFit fit; |
2094 | if (restrictions.fRectsMustMatch == GrSurfaceProxy::RectsMustMatch::kYes) { |
2095 | dstOffset = {0, 0}; |
2096 | fit = SkBackingFit::kExact; |
2097 | } else { |
2098 | dstOffset = {copyRect.fLeft, copyRect.fTop}; |
2099 | fit = SkBackingFit::kApprox; |
2100 | } |
2101 | auto copy = |
2102 | GrSurfaceProxy::Copy(fContext, this->asSurfaceProxy(), this->origin(), GrMipmapped::kNo, |
2103 | copyRect, fit, SkBudgeted::kYes, restrictions.fRectsMustMatch); |
2104 | SkASSERT(copy); |
2105 | |
2106 | dstProxyView->setProxyView({std::move(copy), this->origin(), this->readSwizzle()}); |
2107 | dstProxyView->setOffset(dstOffset); |
2108 | return true; |
2109 | } |
2110 | |
2111 | bool GrRenderTargetContext::blitTexture(GrSurfaceProxyView view, const SkIRect& srcRect, |
2112 | const SkIPoint& dstPoint) { |
2113 | SkASSERT(view.asTextureProxy()); |
2114 | SkIRect clippedSrcRect; |
2115 | SkIPoint clippedDstPoint; |
2116 | if (!GrClipSrcRectAndDstPoint(this->asSurfaceProxy()->dimensions(), view.proxy()->dimensions(), |
2117 | srcRect, dstPoint, &clippedSrcRect, &clippedDstPoint)) { |
2118 | return false; |
2119 | } |
2120 | |
2121 | GrPaint paint; |
2122 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
2123 | |
2124 | auto fp = GrTextureEffect::Make(std::move(view), kUnknown_SkAlphaType); |
2125 | if (!fp) { |
2126 | return false; |
2127 | } |
2128 | paint.setColorFragmentProcessor(std::move(fp)); |
2129 | |
2130 | this->fillRectToRect( |
2131 | nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), |
2132 | SkRect::MakeXYWH(clippedDstPoint.fX, clippedDstPoint.fY, clippedSrcRect.width(), |
2133 | clippedSrcRect.height()), |
2134 | SkRect::Make(clippedSrcRect)); |
2135 | return true; |
2136 | } |
2137 | |
2138 | void GrRenderTargetContext::wasClosed(const GrOpsTask& task) { |
2139 | SkASSERT(&task == fOpsTask.get()); |
2140 | fOpsTask.reset(); |
2141 | } |
2142 | |