Flutter Engine
The Flutter Engine
DrawContext.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 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
9
13
42
43namespace skgpu::graphite {
44
45namespace {
46
47// Discarding content on floating point textures can leave nans as the prior color for a pixel,
48// in which case hardware blending (when enabled) will fail even if the src, dst coefficients
49// and coverage would produce the unmodified src value.
50bool discard_op_should_use_clear(SkColorType ct) {
51 switch(ct) {
57 return true;
58 default:
59 return false;
60 }
61}
62
63} // anonymous namespace
64
67 SkISize deviceSize,
68 const SkColorInfo& colorInfo,
69 const SkSurfaceProps& props) {
70 if (!target) {
71 return nullptr;
72 }
73 // We don't render to unknown or unpremul alphatypes
76 return nullptr;
77 }
78 if (!caps->isRenderable(target->textureInfo())) {
79 return nullptr;
80 }
81
82 // Accept an approximate-fit texture, but make sure it's at least as large as the device's
83 // logical size.
84 // TODO: validate that the color type and alpha type are compatible with the target's info
85 SkASSERT(target->isFullyLazy() || (target->dimensions().width() >= deviceSize.width() &&
86 target->dimensions().height() >= deviceSize.height()));
88 return sk_sp<DrawContext>(new DrawContext(caps, std::move(target), imageInfo, props));
89}
90
91DrawContext::DrawContext(const Caps* caps,
93 const SkImageInfo& ii,
94 const SkSurfaceProps& props)
95 : fTarget(std::move(target))
96 , fImageInfo(ii)
97 , fSurfaceProps(props)
98 , fCurrentDrawTask(sk_make_sp<DrawTask>(fTarget))
99 , fPendingDraws(std::make_unique<DrawList>())
100 , fPendingUploads(std::make_unique<UploadList>()) {
101 if (!caps->isTexturable(fTarget->textureInfo())) {
102 fReadView = {}; // Presumably this DrawContext is rendering into a swap chain
103 } else {
104 Swizzle swizzle = caps->getReadSwizzle(ii.colorType(), fTarget->textureInfo());
105 fReadView = {fTarget, swizzle};
106 }
107 // TBD - Will probably want DrawLists (and its internal commands) to come from an arena
108 // that the DC manages.
109}
110
111DrawContext::~DrawContext() = default;
112
113void DrawContext::clear(const SkColor4f& clearColor) {
114 this->discard();
115
116 fPendingLoadOp = LoadOp::kClear;
117 SkPMColor4f pmColor = clearColor.premul();
118 fPendingClearColor = pmColor.array();
119}
120
122 // Non-loading operations on a fully lazy target can corrupt data beyond the DrawContext's
123 // region so should be avoided.
124 SkASSERT(!fTarget->isFullyLazy());
125
126 // A fullscreen clear or discard will overwrite anything that came before, so clear the DrawList
127 // NOTE: Eventually the current DrawTask should be reset, once there are no longer implicit
128 // dependencies on atlas tasks between DrawContexts. When that's resolved, the only tasks in the
129 // current DrawTask are those that directly impact the target, which becomes irrelevant with the
130 // clear op overwriting it. For now, preserve the previous tasks that might include atlas
131 // uploads that are not explicitly shared between DrawContexts.
132 if (fPendingDraws->renderStepCount() > 0) {
133 fPendingDraws = std::make_unique<DrawList>();
134 }
135 if (fComputePathAtlas) {
136 fComputePathAtlas->reset();
137 }
138
139 if (discard_op_should_use_clear(fImageInfo.colorType())) {
140 // In theory the clear color shouldn't matter since a discardable state should be fully
141 // overwritten by later draws, but if a previous call to clear() had injected bad data,
142 // the discard should not inherit it.
143 fPendingClearColor = {0.f, 0.f, 0.f, 0.f};
144 fPendingLoadOp = LoadOp::kClear;
145 } else {
146 fPendingLoadOp = LoadOp::kDiscard;
147 }
148}
149
151 const Transform& localToDevice,
152 const Geometry& geometry,
153 const Clip& clip,
154 DrawOrder ordering,
155 const PaintParams* paint,
156 const StrokeStyle* stroke) {
157 SkASSERT(SkIRect::MakeSize(this->imageInfo().dimensions()).contains(clip.scissor()));
158 fPendingDraws->recordDraw(renderer, localToDevice, geometry, clip, ordering, paint, stroke);
159}
160
162 sk_sp<TextureProxy> targetProxy,
163 const SkColorInfo& srcColorInfo,
164 const SkColorInfo& dstColorInfo,
165 const std::vector<MipLevel>& levels,
166 const SkIRect& dstRect,
167 std::unique_ptr<ConditionalUploadContext> condContext) {
168 // Our caller should have clipped to the bounds of the surface already.
169 SkASSERT(targetProxy->isFullyLazy() ||
170 SkIRect::MakeSize(targetProxy->dimensions()).contains(dstRect));
171 return fPendingUploads->recordUpload(recorder,
172 std::move(targetProxy),
173 srcColorInfo,
174 dstColorInfo,
175 levels,
176 dstRect,
177 std::move(condContext));
178}
179
181 SkASSERT(task);
182 // Adding `task` to the current DrawTask directly means that it will execute after any previous
183 // dependent tasks and after any previous calls to flush(), but everything else that's being
184 // collected on the DrawContext will execute after `task` once the next flush() is performed.
185 fCurrentDrawTask->addTask(std::move(task));
186}
187
189 if (!fComputePathAtlas) {
190 fComputePathAtlas = recorder->priv().atlasProvider()->createComputePathAtlas(recorder);
191 }
192 return fComputePathAtlas.get();
193}
194
196 if (fPendingUploads->size() > 0) {
198 "# uploads", fPendingUploads->size());
199 fCurrentDrawTask->addTask(UploadTask::Make(fPendingUploads.get()));
200 // The UploadTask steals the collected upload instances, automatically resetting this list
201 SkASSERT(fPendingUploads->size() == 0);
202 }
203
204 // Generate compute dispatches that render into the atlas texture used by pending draws.
205 // TODO: Once compute atlas caching is implemented, DrawContext might not hold onto to this
206 // at which point a recordDispatch() could be added and it stores a pending dispatches list that
207 // much like how uploads are handled. In that case, Device would be responsible for triggering
208 // the recording of dispatches, but that may happen naturally in AtlasProvider::recordUploads().
209 if (fComputePathAtlas) {
211 if (fComputePathAtlas->recordDispatches(recorder, &dispatches)) {
212 // For now this check is valid as all coverage mask draws involve dispatches
213 SkASSERT(fPendingDraws->hasCoverageMaskDraws());
214
215 fCurrentDrawTask->addTask(ComputeTask::Make(std::move(dispatches)));
216 } // else no pending compute work needed to be recorded
217
218 fComputePathAtlas->reset();
219 } // else platform doesn't support compute or atlas was never initialized.
220
221 if (fPendingDraws->renderStepCount() == 0 && fPendingLoadOp != LoadOp::kClear) {
222 // Nothing will be rasterized to the target that warrants a RenderPassTask, but we preserve
223 // any added uploads or compute tasks since those could also affect the target w/o
224 // rasterizing anything directly.
225 return;
226 }
227
228 // Convert the pending draws and load/store ops into a DrawPass that will be executed after
229 // the collected uploads and compute dispatches. If there's a dst readback copy required it
230 // inserts a CopyTextureToTexture task before the RenderPassTask.
231 // TODO: At this point, there's only ever one DrawPass in a RenderPassTask to a target. When
232 // subpasses are implemented, they will either be collected alongside fPendingDraws or added
233 // to the RenderPassTask separately.
234 sk_sp<TextureProxy> dstCopy;
235 SkIRect dstCopyPixelBounds = SkIRect::MakeEmpty();
236 if (!fPendingDraws->dstCopyBounds().isEmptyNegativeOrNaN()) {
237 TRACE_EVENT_INSTANT0("skia.gpu", "DrawPass requires dst copy", TRACE_EVENT_SCOPE_THREAD);
238
239 dstCopyPixelBounds = fPendingDraws->dstCopyBounds().makeRoundOut().asSkIRect();
240
241 // TODO: Right now this assert is ensuring that the dstCopy will be texturable since it
242 // uses the same texture info as fTarget. Ideally, if fTarget were not texturable but
243 // still readable, we would perform a fallback to a compatible texturable info. We also
244 // should decide whether or not a copy-as-draw fallback is necessary here too. All of
245 // this is handled inside Image::Copy() except we would need it to expose the task in
246 // order to link it correctly.
247 SkASSERT(recorder->priv().caps()->isTexturable(fTarget->textureInfo()));
248 dstCopy = TextureProxy::Make(recorder->priv().caps(),
249 recorder->priv().resourceProvider(),
250 dstCopyPixelBounds.size(),
251 fTarget->textureInfo(),
252 "DstCopyTexture",
254 }
255 std::unique_ptr<DrawPass> pass = DrawPass::Make(recorder,
256 std::move(fPendingDraws),
257 fTarget,
258 this->imageInfo(),
259 std::make_pair(fPendingLoadOp, fPendingStoreOp),
260 fPendingClearColor,
261 dstCopy,
262 dstCopyPixelBounds.topLeft());
263 fPendingDraws = std::make_unique<DrawList>();
264 // Now that there is content drawn to the target, that content must be loaded on any subsequent
265 // render pass.
266 fPendingLoadOp = LoadOp::kLoad;
267 fPendingStoreOp = StoreOp::kStore;
268
269 if (pass) {
270 SkASSERT(fTarget.get() == pass->target());
271
272 if (dstCopy) {
273 // Add the copy task to initialize dstCopy before the render pass task.
274 fCurrentDrawTask->addTask(CopyTextureToTextureTask::Make(
275 fTarget, dstCopyPixelBounds, dstCopy, /*dstPoint=*/{0, 0}));
276 }
277
278 const Caps* caps = recorder->priv().caps();
279 auto [loadOp, storeOp] = pass->ops();
280 auto writeSwizzle = caps->getWriteSwizzle(this->colorInfo().colorType(),
281 fTarget->textureInfo());
282
283 RenderPassDesc desc = RenderPassDesc::Make(caps, fTarget->textureInfo(), loadOp, storeOp,
284 pass->depthStencilFlags(),
285 pass->clearColor(),
286 pass->requiresMSAA(),
287 writeSwizzle);
288
290 passes.emplace_back(std::move(pass));
291 fCurrentDrawTask->addTask(RenderPassTask::Make(std::move(passes), desc, fTarget));
292 }
293 // else pass creation failed, DrawPass will have logged why. Don't discard the previously
294 // accumulated tasks, however, since they may represent operations on an atlas that other
295 // DrawContexts now implicitly depend on.
296}
297
299 // If flush() was explicitly called earlier and no new work was recorded, this call to flush()
300 // is a no-op and shouldn't hurt performance.
301 this->flush(recorder);
302
303 if (!fCurrentDrawTask->hasTasks()) {
304 return nullptr;
305 }
306
307 sk_sp<Task> snappedTask = std::move(fCurrentDrawTask);
308 fCurrentDrawTask = sk_make_sp<DrawTask>(fTarget);
309 return snappedTask;
310}
311
312} // namespace skgpu::graphite
kUnpremul_SkAlphaType
@ kUnknown_SkAlphaType
uninitialized
Definition: SkAlphaType.h:27
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkColorType
Definition: SkColorType.h:19
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition: SkColorType.h:38
@ kA16_float_SkColorType
pixel with a half float for alpha
Definition: SkColorType.h:45
@ kRGBA_F32_SkColorType
pixel using C float for red, green, blue, alpha; in 128-bit word
Definition: SkColorType.h:40
@ kRGBA_F16Norm_SkColorType
pixel with half floats in [0,1] for red, green, blue, alpha;
Definition: SkColorType.h:36
@ kR16G16_float_SkColorType
pixel with a half float for red and green
Definition: SkColorType.h:46
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
sk_sp< T > sk_make_sp(Args &&... args)
Definition: SkRefCnt.h:371
#define TRACE_EVENT_SCOPE_THREAD
#define TRACE_FUNC
Definition: SkTraceEvent.h:30
SkAlphaType alphaType() const
Definition: SkImageInfo.h:141
std::unique_ptr< ComputePathAtlas > createComputePathAtlas(Recorder *recorder) const
skgpu::Swizzle getReadSwizzle(SkColorType, const TextureInfo &) const
Definition: Caps.cpp:129
bool isTexturable(const TextureInfo &) const
Definition: Caps.cpp:66
skgpu::Swizzle getWriteSwizzle(SkColorType, const TextureInfo &) const
Definition: Caps.cpp:142
virtual bool isRenderable(const TextureInfo &) const =0
static sk_sp< ComputeTask > Make(DispatchGroupList dispatchGroups)
Definition: ComputeTask.cpp:17
static sk_sp< CopyTextureToTextureTask > Make(sk_sp< TextureProxy > srcProxy, SkIRect srcRect, sk_sp< TextureProxy > dstProxy, SkIPoint dstPoint, int dstLevel=0)
Definition: CopyTask.cpp:123
TextureProxy * target()
Definition: DrawContext.h:56
void recordDependency(sk_sp< Task >)
const SkImageInfo & imageInfo() const
Definition: DrawContext.h:54
void recordDraw(const Renderer *renderer, const Transform &localToDevice, const Geometry &geometry, const Clip &clip, DrawOrder ordering, const PaintParams *paint, const StrokeStyle *stroke)
const SkColorInfo & colorInfo() const
Definition: DrawContext.h:55
static sk_sp< DrawContext > Make(const Caps *caps, sk_sp< TextureProxy > target, SkISize deviceSize, const SkColorInfo &, const SkSurfaceProps &)
Definition: DrawContext.cpp:65
PathAtlas * getComputePathAtlas(Recorder *)
void clear(const SkColor4f &clearColor)
sk_sp< Task > snapDrawTask(Recorder *)
bool recordUpload(Recorder *recorder, sk_sp< TextureProxy > targetProxy, const SkColorInfo &srcColorInfo, const SkColorInfo &dstColorInfo, const std::vector< MipLevel > &levels, const SkIRect &dstRect, std::unique_ptr< ConditionalUploadContext >)
static std::unique_ptr< DrawPass > Make(Recorder *, std::unique_ptr< DrawList >, sk_sp< TextureProxy > target, const SkImageInfo &targetInfo, std::pair< LoadOp, StoreOp >, std::array< float, 4 > clearColor, sk_sp< TextureProxy > dstCopy, SkIPoint dstCopyOffset)
Definition: DrawPass.cpp:458
AtlasProvider * atlasProvider()
Definition: RecorderPriv.h:61
const Caps * caps() const
Definition: RecorderPriv.h:31
ResourceProvider * resourceProvider()
Definition: RecorderPriv.h:33
static sk_sp< RenderPassTask > Make(DrawPassList, const RenderPassDesc &, sk_sp< TextureProxy > target)
static sk_sp< TextureProxy > Make(const Caps *, ResourceProvider *, SkISize dimensions, const TextureInfo &, std::string_view label, skgpu::Budgeted)
const TextureInfo & textureInfo() const
Definition: TextureProxy.h:38
static sk_sp< UploadTask > Make(UploadList *)
Definition: UploadTask.cpp:425
T & emplace_back(Args &&... args)
Definition: SkTArray.h:248
const Paint & paint
Definition: color_source.cc:38
uint32_t * target
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41
Definition: ref_ptr.h:256
Definition: SkRect.h:32
constexpr SkISize size() const
Definition: SkRect.h:172
static constexpr SkIRect MakeSize(const SkISize &size)
Definition: SkRect.h:66
static constexpr SkIRect MakeEmpty()
Definition: SkRect.h:45
constexpr SkIPoint topLeft() const
Definition: SkRect.h:151
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkColorType colorType() const
Definition: SkImageInfo.h:373
std::array< float, 4 > array() const
Definition: SkColor.h:317
static RenderPassDesc Make(const Caps *caps, const TextureInfo &targetInfo, LoadOp loadOp, StoreOp storeOp, SkEnumBitMask< DepthStencilFlags > depthStencilFlags, const std::array< float, 4 > &clearColor, bool requiresMSAA, Swizzle writeSwizzle)
#define TRACE_EVENT_INSTANT0(category_group, name)
Definition: trace_event.h:175
#define TRACE_EVENT_INSTANT1(category_group, name, arg1_name, arg1_val)
Definition: trace_event.h:179