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
8 #include "src/gpu/graphite/DrawContext.h"
9
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkPixmap.h"
12 #include "include/private/SkColorData.h"
13
14 #include "include/gpu/graphite/Context.h"
15 #include "include/gpu/graphite/Recorder.h"
16 #include "src/gpu/graphite/Buffer.h"
17 #include "src/gpu/graphite/Caps.h"
18 #include "src/gpu/graphite/CommandBuffer.h"
19 #include "src/gpu/graphite/ContextPriv.h"
20 #include "src/gpu/graphite/DrawList.h"
21 #include "src/gpu/graphite/DrawPass.h"
22 #include "src/gpu/graphite/RecorderPriv.h"
23 #include "src/gpu/graphite/RenderPassTask.h"
24 #include "src/gpu/graphite/ResourceTypes.h"
25 #include "src/gpu/graphite/SharedContext.h"
26 #include "src/gpu/graphite/TextureProxy.h"
27 #include "src/gpu/graphite/TextureProxyView.h"
28 #include "src/gpu/graphite/UploadTask.h"
29 #include "src/gpu/graphite/geom/BoundsManager.h"
30 #include "src/gpu/graphite/geom/Geometry.h"
31 #include "src/gpu/graphite/text/AtlasManager.h"
32
33 #ifdef SK_ENABLE_PIET_GPU
34 #include "src/gpu/graphite/PietRenderTask.h"
35 #endif
36
37 namespace skgpu::graphite {
38
Make(sk_sp<TextureProxy> target,SkISize deviceSize,const SkColorInfo & colorInfo,const SkSurfaceProps & props)39 sk_sp<DrawContext> DrawContext::Make(sk_sp<TextureProxy> target,
40 SkISize deviceSize,
41 const SkColorInfo& colorInfo,
42 const SkSurfaceProps& props) {
43 if (!target) {
44 return nullptr;
45 }
46
47 // TODO: validate that the color type and alpha type are compatible with the target's info
48 SkASSERT(!target->isInstantiated() || target->dimensions() == deviceSize);
49 SkImageInfo imageInfo = SkImageInfo::Make(deviceSize, colorInfo);
50 return sk_sp<DrawContext>(new DrawContext(std::move(target), imageInfo, props));
51 }
52
DrawContext(sk_sp<TextureProxy> target,const SkImageInfo & ii,const SkSurfaceProps & props)53 DrawContext::DrawContext(sk_sp<TextureProxy> target,
54 const SkImageInfo& ii,
55 const SkSurfaceProps& props)
56 : fTarget(std::move(target))
57 , fImageInfo(ii)
58 , fSurfaceProps(props)
59 , fPendingDraws(std::make_unique<DrawList>())
60 , fPendingUploads(std::make_unique<UploadList>()) {
61 // TBD - Will probably want DrawLists (and its internal commands) to come from an arena
62 // that the DC manages.
63 }
64
~DrawContext()65 DrawContext::~DrawContext() {
66 // If the DC is destroyed and there are pending commands, they won't be drawn.
67 fPendingDraws.reset();
68 fDrawPasses.clear();
69 }
70
readSurfaceView(const Caps * caps)71 TextureProxyView DrawContext::readSurfaceView(const Caps* caps) {
72 TextureProxy* proxy = this->target();
73
74 if (!caps->isTexturable(proxy->textureInfo())) {
75 return {};
76 }
77
78 Swizzle swizzle = caps->getReadSwizzle(this->imageInfo().colorType(),
79 proxy->textureInfo());
80
81 return TextureProxyView(sk_ref_sp(proxy), swizzle);
82 }
83
clear(const SkColor4f & clearColor)84 void DrawContext::clear(const SkColor4f& clearColor) {
85 fPendingLoadOp = LoadOp::kClear;
86 SkPMColor4f pmColor = clearColor.premul();
87 fPendingClearColor = pmColor.array();
88
89 // a fullscreen clear will overwrite anything that came before, so start a new DrawList
90 // and clear any drawpasses that haven't been snapped yet
91 fPendingDraws = std::make_unique<DrawList>();
92 fDrawPasses.clear();
93 }
94
recordDraw(const Renderer * renderer,const Transform & localToDevice,const Geometry & geometry,const Clip & clip,DrawOrder ordering,const PaintParams * paint,const StrokeStyle * stroke)95 void DrawContext::recordDraw(const Renderer* renderer,
96 const Transform& localToDevice,
97 const Geometry& geometry,
98 const Clip& clip,
99 DrawOrder ordering,
100 const PaintParams* paint,
101 const StrokeStyle* stroke) {
102 SkASSERT(SkIRect::MakeSize(this->imageInfo().dimensions()).contains(clip.scissor()));
103 fPendingDraws->recordDraw(renderer, localToDevice, geometry, clip, ordering, paint, stroke);
104 }
105
recordTextUploads(AtlasManager * am)106 bool DrawContext::recordTextUploads(AtlasManager* am) {
107 return am->recordUploads(fPendingUploads.get(), /*useCachedUploads=*/false);
108 }
109
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> condContext)110 bool DrawContext::recordUpload(Recorder* recorder,
111 sk_sp<TextureProxy> targetProxy,
112 const SkColorInfo& srcColorInfo,
113 const SkColorInfo& dstColorInfo,
114 const std::vector<MipLevel>& levels,
115 const SkIRect& dstRect,
116 std::unique_ptr<ConditionalUploadContext> condContext) {
117 // Our caller should have clipped to the bounds of the surface already.
118 SkASSERT(targetProxy->isFullyLazy() ||
119 SkIRect::MakeSize(targetProxy->dimensions()).contains(dstRect));
120 return fPendingUploads->recordUpload(recorder,
121 std::move(targetProxy),
122 srcColorInfo,
123 dstColorInfo,
124 levels,
125 dstRect,
126 std::move(condContext));
127 }
128
129 #ifdef SK_ENABLE_PIET_GPU
recordPietSceneRender(Recorder *,sk_sp<TextureProxy> targetProxy,sk_sp<const skgpu::piet::Scene> scene)130 bool DrawContext::recordPietSceneRender(Recorder*,
131 sk_sp<TextureProxy> targetProxy,
132 sk_sp<const skgpu::piet::Scene> scene) {
133 fPendingPietRenders.push_back(PietRenderInstance(std::move(scene), std::move(targetProxy)));
134 return true;
135 }
136 #endif
137
snapDrawPass(Recorder * recorder)138 void DrawContext::snapDrawPass(Recorder* recorder) {
139 if (fPendingDraws->drawCount() == 0 && fPendingLoadOp != LoadOp::kClear) {
140 return;
141 }
142
143 auto pass = DrawPass::Make(recorder,
144 std::move(fPendingDraws),
145 fTarget,
146 this->imageInfo(),
147 std::make_pair(fPendingLoadOp, fPendingStoreOp),
148 fPendingClearColor);
149 fDrawPasses.push_back(std::move(pass));
150 fPendingDraws = std::make_unique<DrawList>();
151 fPendingLoadOp = LoadOp::kLoad;
152 fPendingStoreOp = StoreOp::kStore;
153 }
154
Make(const Caps * caps,const TextureInfo & targetInfo,LoadOp loadOp,StoreOp storeOp,SkEnumBitMask<DepthStencilFlags> depthStencilFlags,const std::array<float,4> & clearColor,bool requiresMSAA)155 RenderPassDesc RenderPassDesc::Make(const Caps* caps,
156 const TextureInfo& targetInfo,
157 LoadOp loadOp,
158 StoreOp storeOp,
159 SkEnumBitMask<DepthStencilFlags> depthStencilFlags,
160 const std::array<float, 4>& clearColor,
161 bool requiresMSAA) {
162 RenderPassDesc desc;
163
164 // It doesn't make sense to have a storeOp for our main target not be store. Why are we doing
165 // this DrawPass then
166 SkASSERT(storeOp == StoreOp::kStore);
167 if (requiresMSAA) {
168 // TODO: If the resolve texture isn't readable, the MSAA color attachment will need to be
169 // persistently associated with the framebuffer, in which case it's not discardable.
170 desc.fColorAttachment.fTextureInfo = caps->getDefaultMSAATextureInfo(targetInfo,
171 Discardable::kYes);
172 if (loadOp != LoadOp::kClear) {
173 desc.fColorAttachment.fLoadOp = LoadOp::kDiscard;
174 } else {
175 desc.fColorAttachment.fLoadOp = LoadOp::kClear;
176 }
177 desc.fColorAttachment.fStoreOp = StoreOp::kDiscard;
178
179 desc.fColorResolveAttachment.fTextureInfo = targetInfo;
180 if (loadOp != LoadOp::kLoad) {
181 desc.fColorResolveAttachment.fLoadOp = LoadOp::kDiscard;
182 } else {
183 desc.fColorResolveAttachment.fLoadOp = LoadOp::kLoad;
184 }
185 desc.fColorResolveAttachment.fStoreOp = storeOp;
186 } else {
187 desc.fColorAttachment.fTextureInfo = targetInfo;
188 desc.fColorAttachment.fLoadOp = loadOp;
189 desc.fColorAttachment.fStoreOp = storeOp;
190 }
191 desc.fClearColor = clearColor;
192
193 if (depthStencilFlags != DepthStencilFlags::kNone) {
194 desc.fDepthStencilAttachment.fTextureInfo = caps->getDefaultDepthStencilTextureInfo(
195 depthStencilFlags,
196 desc.fColorAttachment.fTextureInfo.numSamples(),
197 Protected::kNo);
198 // Always clear the depth and stencil to 0 at the start of a DrawPass, but discard at the
199 // end since their contents do not affect the next frame.
200 desc.fDepthStencilAttachment.fLoadOp = LoadOp::kClear;
201 desc.fClearDepth = 0.f;
202 desc.fClearStencil = 0;
203 desc.fDepthStencilAttachment.fStoreOp = StoreOp::kDiscard;
204 }
205
206 return desc;
207 }
208
snapRenderPassTask(Recorder * recorder)209 sk_sp<Task> DrawContext::snapRenderPassTask(Recorder* recorder) {
210 this->snapDrawPass(recorder);
211 if (fDrawPasses.empty()) {
212 return nullptr;
213 }
214
215 const Caps* caps = recorder->priv().caps();
216
217 // TODO: At this point we would determine all the targets used by the drawPasses,
218 // build up the union of them and store them in the RenderPassDesc. However, for
219 // the moment we should have only one drawPass.
220 SkASSERT(fDrawPasses.size() == 1);
221 auto& drawPass = fDrawPasses[0];
222 const TextureInfo& targetInfo = drawPass->target()->textureInfo();
223 auto [loadOp, storeOp] = drawPass->ops();
224
225 RenderPassDesc desc = RenderPassDesc::Make(caps, targetInfo, loadOp, storeOp,
226 drawPass->depthStencilFlags(),
227 drawPass->clearColor(),
228 drawPass->requiresMSAA());
229
230 sk_sp<TextureProxy> targetProxy = sk_ref_sp(fDrawPasses[0]->target());
231 return RenderPassTask::Make(std::move(fDrawPasses), desc, std::move(targetProxy));
232 }
233
snapUploadTask(Recorder * recorder)234 sk_sp<Task> DrawContext::snapUploadTask(Recorder* recorder) {
235 if (!fPendingUploads || fPendingUploads->size() == 0) {
236 return nullptr;
237 }
238
239 sk_sp<Task> uploadTask = UploadTask::Make(fPendingUploads.get());
240
241 fPendingUploads = std::make_unique<UploadList>();
242
243 return uploadTask;
244 }
245
246 #ifdef SK_ENABLE_PIET_GPU
snapPietRenderTask(Recorder * recorder)247 sk_sp<Task> DrawContext::snapPietRenderTask(Recorder* recorder) {
248 if (fPendingPietRenders.empty()) {
249 return nullptr;
250 }
251 return sk_sp<Task>(new PietRenderTask(std::move(fPendingPietRenders)));
252 }
253 #endif
254
255 } // namespace skgpu::graphite
256