blob: 49bf84d7a3455dc58f5869c4a0d1da2b35cf032f [file] [log] [blame]
Brian Salomon590f5672020-12-16 11:44:47 -05001/*
2 * Copyright 2020 Google LLC
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/GrSurfaceFillContext.h"
9
10#include "include/private/GrImageContext.h"
11#include "src/gpu/GrImageContextPriv.h"
12#include "src/gpu/GrProxyProvider.h"
13#include "src/gpu/GrSurfaceDrawContext.h"
14#include "src/gpu/ops/GrClearOp.h"
15#include "src/gpu/ops/GrFillRectOp.h"
16
17#define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(this->singleOwner())
18#define RETURN_IF_ABANDONED if (fContext->abandoned()) { return; }
19
20class AutoCheckFlush {
21public:
22 AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) {
23 SkASSERT(fDrawingManager);
24 }
25 ~AutoCheckFlush() { fDrawingManager->flushIfNecessary(); }
26
27private:
28 GrDrawingManager* fDrawingManager;
29};
30
31static inline GrColorType color_type_fallback(GrColorType ct) {
32 switch (ct) {
33 // kRGBA_8888 is our default fallback for many color types that may not have renderable
34 // backend formats.
35 case GrColorType::kAlpha_8:
36 case GrColorType::kBGR_565:
37 case GrColorType::kABGR_4444:
38 case GrColorType::kBGRA_8888:
39 case GrColorType::kRGBA_1010102:
40 case GrColorType::kBGRA_1010102:
41 case GrColorType::kRGBA_F16:
42 case GrColorType::kRGBA_F16_Clamped:
43 return GrColorType::kRGBA_8888;
44 case GrColorType::kAlpha_F16:
45 return GrColorType::kRGBA_F16;
46 case GrColorType::kGray_8:
47 return GrColorType::kRGB_888x;
48 default:
49 return GrColorType::kUnknown;
50 }
51}
52
53std::tuple<GrColorType, GrBackendFormat> GrSurfaceFillContext::GetFallbackColorTypeAndFormat(
54 GrImageContext* context, GrColorType colorType, int sampleCnt) {
55 auto caps = context->priv().caps();
56 do {
57 auto format = caps->getDefaultBackendFormat(colorType, GrRenderable::kYes);
58 // We continue to the fallback color type if there no default renderable format or we
59 // requested msaa and the format doesn't support msaa.
60 if (format.isValid() && caps->isFormatRenderable(format, sampleCnt)) {
61 return {colorType, format};
62 }
63 colorType = color_type_fallback(colorType);
64 } while (colorType != GrColorType::kUnknown);
65 return {GrColorType::kUnknown, {}};
66}
67
68std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::Make(GrRecordingContext* context,
69 SkAlphaType alphaType,
70 sk_sp<SkColorSpace> colorSpace,
71 SkISize dimensions,
72 SkBackingFit fit,
73 const GrBackendFormat& format,
74 int sampleCount,
75 GrMipmapped mipmapped,
76 GrProtected isProtected,
77 GrSwizzle readSwizzle,
78 GrSwizzle writeSwizzle,
79 GrSurfaceOrigin origin,
80 SkBudgeted budgeted) {
81 SkASSERT(context);
82 SkASSERT(!dimensions.isEmpty());
83 SkASSERT(sampleCount >= 1);
84 SkASSERT(format.isValid() && format.backend() == context->backend());
85 if (alphaType == kPremul_SkAlphaType || alphaType == kOpaque_SkAlphaType) {
86 return GrSurfaceDrawContext::Make(context,
87 std::move(colorSpace),
88 fit,
89 dimensions,
90 format,
91 sampleCount,
92 mipmapped,
93 isProtected,
94 readSwizzle,
95 writeSwizzle,
96 origin,
97 budgeted,
98 /* surface props*/ nullptr);
99 }
100
101 sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy(format,
102 dimensions,
103 GrRenderable::kYes,
104 sampleCount,
105 mipmapped,
106 fit,
107 budgeted,
108 isProtected);
109 if (!proxy) {
110 return nullptr;
111 }
112 GrImageInfo info(GrColorType::kUnknown, alphaType, std::move(colorSpace), dimensions);
113 GrSurfaceProxyView readView( proxy, origin, readSwizzle);
114 GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
115 auto fillContext = std::make_unique<GrSurfaceFillContext>(context,
116 std::move(readView),
117 std::move(writeView),
118 info.colorInfo());
119 fillContext->discard();
120 return fillContext;
121}
122
123std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::Make(GrRecordingContext* context,
124 GrImageInfo info,
125 SkBackingFit fit,
126 int sampleCount,
127 GrMipmapped mipmapped,
128 GrProtected isProtected,
129 GrSurfaceOrigin origin,
130 SkBudgeted budgeted) {
131 if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
132 return GrSurfaceDrawContext::Make(context,
133 info.colorType(),
134 info.refColorSpace(),
135 fit,
136 info.dimensions(),
137 sampleCount,
138 mipmapped,
139 isProtected,
140 origin,
141 budgeted,
142 nullptr);
143 }
144 GrBackendFormat format = context->priv().caps()->getDefaultBackendFormat(info.colorType(),
145 GrRenderable::kYes);
146 sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy(format,
147 info.dimensions(),
148 GrRenderable::kYes,
149 sampleCount,
150 mipmapped,
151 fit,
152 budgeted,
153 isProtected);
154 if (!proxy) {
155 return nullptr;
156 }
157 GrSwizzle readSwizzle = context->priv().caps()->getReadSwizzle (format, info.colorType());
158 GrSwizzle writeSwizzle = context->priv().caps()->getWriteSwizzle(format, info.colorType());
159
160 GrSurfaceProxyView readView( proxy, origin, readSwizzle);
161 GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
162 auto fillContext = std::make_unique<GrSurfaceFillContext>(context,
163 std::move(readView),
164 std::move(writeView),
165 info.colorInfo());
166 fillContext->discard();
167 return fillContext;
168}
169
170std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::MakeWithFallback(
171 GrRecordingContext* context,
172 GrImageInfo info,
173 SkBackingFit fit,
174 int sampleCount,
175 GrMipmapped mipmapped,
176 GrProtected isProtected,
177 GrSurfaceOrigin origin,
178 SkBudgeted budgeted) {
179 if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
180 return GrSurfaceDrawContext::MakeWithFallback(context,
181 info.colorType(),
182 info.refColorSpace(),
183 fit,
184 info.dimensions(),
185 sampleCount,
186 mipmapped,
187 isProtected,
188 origin,
189 budgeted,
190 nullptr);
191 }
192 auto [ct, _] = GetFallbackColorTypeAndFormat(context, info.colorType(), sampleCount);
193 if (ct == GrColorType::kUnknown) {
194 return nullptr;
195 }
196 info = info.makeColorType(ct);
197 return GrSurfaceFillContext::Make(context,
198 info,
199 fit,
200 sampleCount,
201 mipmapped,
202 isProtected,
203 origin,
204 budgeted);
205}
206
207std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::MakeFromBackendTexture(
208 GrRecordingContext* context,
209 GrColorInfo info,
210 const GrBackendTexture& tex,
211 int sampleCount,
212 GrSurfaceOrigin origin,
213 sk_sp<GrRefCntedCallback> releaseHelper) {
214 SkASSERT(sampleCount > 0);
215
216 if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
217 return GrSurfaceDrawContext::MakeFromBackendTexture(context,
218 info.colorType(),
219 info.refColorSpace(),
220 tex,
221 sampleCount,
222 origin,
223 nullptr,
224 std::move(releaseHelper));
225 }
226 const GrBackendFormat& format = tex.getBackendFormat();
227 GrSwizzle readSwizzle, writeSwizzle;
228 if (info.colorType() != GrColorType::kUnknown) {
229 if (!context->priv().caps()->areColorTypeAndFormatCompatible(info.colorType(), format)) {
230 return nullptr;
231 }
232 readSwizzle = context->priv().caps()->getReadSwizzle (format, info.colorType());
233 writeSwizzle = context->priv().caps()->getWriteSwizzle(format, info.colorType());
234 }
235
236 sk_sp<GrTextureProxy> proxy(context->priv().proxyProvider()->wrapRenderableBackendTexture(
237 tex, sampleCount, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo,
238 std::move(releaseHelper)));
239 if (!proxy) {
240 return nullptr;
241 }
242
243 GrSurfaceProxyView readView( proxy, origin, readSwizzle);
244 GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
245
246 return std::make_unique<GrSurfaceFillContext>(context,
247 std::move(readView),
248 std::move(writeView),
249 std::move(info));
250
251}
252
253// In MDB mode the reffing of the 'getLastOpsTask' call's result allows in-progress
254// GrOpsTask to be picked up and added to by GrSurfaceFillContext lower in the call
255// stack. When this occurs with a closed GrOpsTask, a new one will be allocated
256// when the GrSurfaceFillContext attempts to use it (via getOpsTask).
257GrSurfaceFillContext::GrSurfaceFillContext(GrRecordingContext* context,
258 GrSurfaceProxyView readView,
259 GrSurfaceProxyView writeView,
260 const GrColorInfo& colorInfo,
261 bool flushTimeOpsTask)
262 : GrSurfaceContext(context, std::move(readView), std::move(colorInfo))
263 , fWriteView(std::move(writeView))
264 , fFlushTimeOpsTask(flushTimeOpsTask) {
265 fOpsTask = sk_ref_sp(context->priv().drawingManager()->getLastOpsTask(this->asSurfaceProxy()));
266 SkASSERT(this->asSurfaceProxy() == fWriteView.proxy());
267 SkASSERT(this->origin() == fWriteView.origin());
268
269 SkDEBUGCODE(this->validate();)
270}
271
272void GrSurfaceFillContext::fillRectWithFP(const SkIRect& dstRect,
273 std::unique_ptr<GrFragmentProcessor> fp) {
274 ASSERT_SINGLE_OWNER
275 RETURN_IF_ABANDONED
276 SkDEBUGCODE(this->validate();)
277 GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceFillContext", "fillRectWithFP", fContext);
278
279 AutoCheckFlush acf(this->drawingManager());
280
281 GrPaint paint;
282 paint.setColorFragmentProcessor(std::move(fp));
283 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
284 auto op = GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
285 SkRect::Make(dstRect));
286 this->addDrawOp(std::move(op));
287}
288
289void GrSurfaceFillContext::addDrawOp(GrOp::Owner owner) {
290 GrDrawOp* op = static_cast<GrDrawOp*>(owner.get());
291 GrClampType clampType = GrColorTypeClampType(this->colorInfo().colorType());
292 auto clip = GrAppliedClip::Disabled();
293 const GrCaps& caps = *this->caps();
294 GrProcessorSet::Analysis analysis = op->finalize(caps,
295 &clip,
296 /*mixed sample coverage*/ false,
297 clampType);
298 SkASSERT(!(op->fixedFunctionFlags() & GrDrawOp::FixedFunctionFlags::kUsesStencil));
299 SkASSERT(!analysis.requiresDstTexture());
300 SkRect bounds = owner->bounds();
301 // We shouldn't have coverage AA or hairline draws in fill contexts.
302 SkASSERT(!op->hasAABloat() && !op->hasZeroArea());
303 if (!bounds.intersect(this->asSurfaceProxy()->getBoundsRect())) {
304 return;
305 }
306 op->setClippedBounds(op->bounds());
307 SkDEBUGCODE(op->fAddDrawOpCalled = true;)
308
309 GrXferProcessor::DstProxyView dstProxyView;
310 this->getOpsTask()->addDrawOp(fContext->priv().drawingManager(),
311 std::move(owner),
312 analysis,
313 std::move(clip),
314 dstProxyView,
315 GrTextureResolveManager(this->drawingManager()),
316 caps);
317}
318
319void GrSurfaceFillContext::ClearToGrPaint(std::array<float, 4> color, GrPaint* paint) {
320 paint->setColor4f({color[0], color[1], color[2], color[3]});
321 if (color[3] == 1.f) {
322 // Can just rely on the src-over blend mode to do the right thing.
323 // This may improve batching.
324 paint->setPorterDuffXPFactory(SkBlendMode::kSrcOver);
325 } else {
326 // A clear overwrites the prior color, so even if it's transparent, it behaves as if it
327 // were src blended
328 paint->setPorterDuffXPFactory(SkBlendMode::kSrc);
329 }
330}
331
332void GrSurfaceFillContext::addOp(GrOp::Owner op) {
333 GrDrawingManager* drawingMgr = this->drawingManager();
334 this->getOpsTask()->addOp(drawingMgr,
335 std::move(op),
336 GrTextureResolveManager(drawingMgr),
337 *this->caps());
338}
339
340GrOpsTask* GrSurfaceFillContext::getOpsTask() {
341 ASSERT_SINGLE_OWNER
342 SkDEBUGCODE(this->validate();)
343
344 if (!fOpsTask || fOpsTask->isClosed()) {
345 sk_sp<GrOpsTask> newOpsTask = this->drawingManager()->newOpsTask(this->writeSurfaceView(),
346 fFlushTimeOpsTask);
347 if (fOpsTask) {
348 this->willReplaceOpsTask(fOpsTask.get(), newOpsTask.get());
349 }
350 fOpsTask = std::move(newOpsTask);
351 }
352 SkASSERT(!fOpsTask->isClosed());
353 return fOpsTask.get();
354}
355
356#ifdef SK_DEBUG
357void GrSurfaceFillContext::onValidate() const {
358 if (fOpsTask && !fOpsTask->isClosed()) {
359 SkASSERT(this->drawingManager()->getLastRenderTask(fWriteView.proxy()) == fOpsTask.get());
360 }
361}
362#endif
363
364void GrSurfaceFillContext::discard() {
365 ASSERT_SINGLE_OWNER
366 RETURN_IF_ABANDONED
367 SkDEBUGCODE(this->validate();)
368 GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceDrawContext", "discard", fContext);
369
370 AutoCheckFlush acf(this->drawingManager());
371
372 this->getOpsTask()->discard();
373}
374
375void GrSurfaceFillContext::internalClear(const SkIRect* scissor,
376 std::array<float, 4> color,
377 bool upgradePartialToFull) {
378 ASSERT_SINGLE_OWNER
379 RETURN_IF_ABANDONED
380 SkDEBUGCODE(this->validate();)
381 GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceDrawContext", "clear", fContext);
382
383 // There are three ways clears are handled: load ops, native clears, and draws. Load ops are
384 // only for fullscreen clears; native clears can be fullscreen or with scissors if the backend
385 // supports then. Drawing an axis-aligned rect is the fallback path.
386 GrScissorState scissorState(this->asSurfaceProxy()->backingStoreDimensions());
387 if (scissor && !scissorState.set(*scissor)) {
388 // The clear is offscreen, so skip it (normally this would be handled by addDrawOp,
389 // except clear ops are not draw ops).
390 return;
391 }
392
393 // If we have a scissor but it's okay to clear beyond it for performance reasons, then disable
394 // the test. We only do this when the clear would be handled by a load op or natively.
395 if (scissorState.enabled() && !this->caps()->performColorClearsAsDraws()) {
396 if (upgradePartialToFull && (this->caps()->preferFullscreenClears() ||
397 this->caps()->shouldInitializeTextures())) {
398 // TODO: wrt the shouldInitializeTextures path, it would be more performant to
399 // only clear the entire target if we knew it had not been cleared before. As
400 // is this could end up doing a lot of redundant clears.
401 scissorState.setDisabled();
402 } else {
403 // Unlike with stencil clears, we also allow clears up to the logical dimensions of the
404 // render target to overflow into any approx-fit padding of the backing store dimensions
405 scissorState.relaxTest(this->dimensions());
406 }
407 }
408
409 if (!scissorState.enabled()) {
410 // This is a fullscreen clear, so could be handled as a load op. Regardless, we can also
411 // discard all prior ops in the current task since the color buffer will be overwritten.
412 GrOpsTask* opsTask = this->getOpsTask();
413 if (opsTask->resetForFullscreenClear(this->canDiscardPreviousOpsOnFullClear()) &&
414 !this->caps()->performColorClearsAsDraws()) {
415 color = this->writeSurfaceView().swizzle().applyTo(color);
416 // The op list was emptied and native clears are allowed, so just use the load op
417 opsTask->setColorLoadOp(GrLoadOp::kClear, color);
418 return;
419 } else {
420 // Will use an op for the clear, reset the load op to discard since the op will
421 // blow away the color buffer contents
422 opsTask->setColorLoadOp(GrLoadOp::kDiscard);
423 }
424 }
425
426 // At this point we are either a partial clear or a fullscreen clear that couldn't be applied
427 // as a load op.
428 bool clearAsDraw = this->caps()->performColorClearsAsDraws() ||
429 (scissorState.enabled() && this->caps()->performPartialClearsAsDraws());
430 if (clearAsDraw) {
431 GrPaint paint;
432 ClearToGrPaint(color, &paint);
433 auto op = GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
434 SkRect::Make(scissorState.rect()));
435 this->addDrawOp(std::move(op));
436 } else {
437 color = this->writeSurfaceView().swizzle().applyTo(color);
438 this->addOp(GrClearOp::MakeColor(fContext, scissorState, color));
439 }
440}
441
442bool GrSurfaceFillContext::blitTexture(GrSurfaceProxyView view,
443 const SkIRect& srcRect,
444 const SkIPoint& dstPoint) {
445 SkASSERT(view.asTextureProxy());
446 SkIRect clippedSrcRect;
447 SkIPoint clippedDstPoint;
448 if (!GrClipSrcRectAndDstPoint(this->dimensions(),
449 view.dimensions(),
450 srcRect,
451 dstPoint,
452 &clippedSrcRect,
453 &clippedDstPoint)) {
454 return false;
455 }
456
457 auto fp = GrTextureEffect::Make(std::move(view), kUnknown_SkAlphaType);
458 auto dstRect = SkIRect::MakePtSize(clippedDstPoint, clippedSrcRect.size());
459 auto srcRectF = SkRect::Make(clippedSrcRect);
460 this->fillRectToRectWithFP(srcRectF, dstRect, std::move(fp));
461 return true;
462}