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/CommandBuffer.h"
9
10 #include "src/core/SkTraceEvent.h"
11 #include "src/gpu/RefCntedCallback.h"
12 #include "src/gpu/graphite/Buffer.h"
13 #include "src/gpu/graphite/ComputePipeline.h"
14 #include "src/gpu/graphite/DrawPass.h"
15 #include "src/gpu/graphite/GraphicsPipeline.h"
16 #include "src/gpu/graphite/Log.h"
17 #include "src/gpu/graphite/RenderPassDesc.h"
18 #include "src/gpu/graphite/ResourceProvider.h"
19 #include "src/gpu/graphite/Sampler.h"
20 #include "src/gpu/graphite/Texture.h"
21 #include "src/gpu/graphite/TextureProxy.h"
22
23 namespace skgpu::graphite {
24
CommandBuffer(Protected isProtected)25 CommandBuffer::CommandBuffer(Protected isProtected) : fIsProtected(isProtected) {}
26
~CommandBuffer()27 CommandBuffer::~CommandBuffer() {
28 this->releaseResources();
29 }
30
releaseResources()31 void CommandBuffer::releaseResources() {
32 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
33
34 fTrackedUsageResources.clear();
35 fCommandBufferResources.clear();
36 }
37
resetCommandBuffer()38 void CommandBuffer::resetCommandBuffer() {
39 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
40
41 // The dst copy texture and sampler are kept alive by the tracked resources, so reset these
42 // before we release their refs. Assuming we don't go idle and free lots of resources, we'll
43 // get the same cached sampler the next time we need a dst copy.
44 fDstCopy = {nullptr, nullptr};
45 this->releaseResources();
46 this->onResetCommandBuffer();
47 fBuffersToAsyncMap.clear();
48 }
49
trackResource(sk_sp<Resource> resource)50 void CommandBuffer::trackResource(sk_sp<Resource> resource) {
51 fTrackedUsageResources.push_back(std::move(resource));
52 }
53
trackCommandBufferResource(sk_sp<Resource> resource)54 void CommandBuffer::trackCommandBufferResource(sk_sp<Resource> resource) {
55 fCommandBufferResources.push_back(std::move(resource));
56 }
57
addFinishedProc(sk_sp<RefCntedCallback> finishedProc)58 void CommandBuffer::addFinishedProc(sk_sp<RefCntedCallback> finishedProc) {
59 fFinishedProcs.push_back(std::move(finishedProc));
60 }
61
callFinishedProcs(bool success)62 void CommandBuffer::callFinishedProcs(bool success) {
63 if (!success) {
64 for (int i = 0; i < fFinishedProcs.size(); ++i) {
65 fFinishedProcs[i]->setFailureResult();
66 }
67 } else {
68 if (auto stats = this->gpuStats()) {
69 for (int i = 0; i < fFinishedProcs.size(); ++i) {
70 if (fFinishedProcs[i]->receivesGpuStats()) {
71 fFinishedProcs[i]->setStats(*stats);
72 }
73 }
74 }
75 }
76 fFinishedProcs.clear();
77 }
78
addBuffersToAsyncMapOnSubmit(SkSpan<const sk_sp<Buffer>> buffers)79 void CommandBuffer::addBuffersToAsyncMapOnSubmit(SkSpan<const sk_sp<Buffer>> buffers) {
80 for (size_t i = 0; i < buffers.size(); ++i) {
81 SkASSERT(buffers[i]);
82 fBuffersToAsyncMap.push_back(buffers[i]);
83 }
84 }
85
buffersToAsyncMapOnSubmit() const86 SkSpan<const sk_sp<Buffer>> CommandBuffer::buffersToAsyncMapOnSubmit() const {
87 return fBuffersToAsyncMap;
88 }
89
addRenderPass(const RenderPassDesc & renderPassDesc,sk_sp<Texture> colorTexture,sk_sp<Texture> resolveTexture,sk_sp<Texture> depthStencilTexture,const Texture * dstCopy,SkIRect dstReadBounds,SkISize viewportDims,const DrawPassList & drawPasses)90 bool CommandBuffer::addRenderPass(const RenderPassDesc& renderPassDesc,
91 sk_sp<Texture> colorTexture,
92 sk_sp<Texture> resolveTexture,
93 sk_sp<Texture> depthStencilTexture,
94 const Texture* dstCopy,
95 SkIRect dstReadBounds,
96 SkISize viewportDims,
97 const DrawPassList& drawPasses) {
98 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
99
100 SkIRect renderPassBounds;
101 for (const auto& drawPass : drawPasses) {
102 renderPassBounds.join(drawPass->bounds());
103 }
104 if (renderPassDesc.fColorAttachment.fLoadOp == LoadOp::kClear) {
105 renderPassBounds.join(fRenderPassBounds);
106 }
107 renderPassBounds.offset(fReplayTranslation.x(), fReplayTranslation.y());
108 if (!renderPassBounds.intersect(fRenderPassBounds)) {
109 // The entire RenderPass is offscreen given the replay translation so skip adding the pass
110 // at all
111 return true;
112 }
113
114 dstReadBounds.offset(fReplayTranslation.x(), fReplayTranslation.y());
115 if (!dstReadBounds.intersect(fRenderPassBounds)) {
116 // The draws within the RenderPass that would sample from the dstCopy have been translated
117 // off screen. Set the bounds to empty and let the GPU clipping do its job.
118 dstReadBounds = SkIRect::MakeEmpty();
119 }
120 // Save the dstCopy texture so that it can be embedded into texture bind commands later on.
121 // Stash the texture's full dimensions on the rect so we can calculate normalized coords later.
122 fDstCopy.first = dstCopy;
123 fDstReadBounds = dstCopy ? SkIRect::MakePtSize(dstReadBounds.topLeft(), dstCopy->dimensions())
124 : SkIRect::MakeEmpty();
125 if (dstCopy && !fDstCopy.second) {
126 // Only lookup the sampler the first time we require a dstCopy. The texture can change
127 // on subsequent passes but it will always use the same nearest neighbor sampling.
128 sk_sp<Sampler> nearestNeighbor = this->resourceProvider()->findOrCreateCompatibleSampler(
129 {SkFilterMode::kNearest, SkTileMode::kClamp});
130 fDstCopy.second = nearestNeighbor.get();
131 this->trackResource(std::move(nearestNeighbor));
132 }
133
134 // We don't intersect the viewport with the render pass bounds or target size because it just
135 // defines a linear transform, which we don't want to change just because a portion of it maps
136 // to a region that gets clipped.
137 SkIRect viewport = SkIRect::MakePtSize(fReplayTranslation, viewportDims);
138 if (!this->onAddRenderPass(renderPassDesc,
139 renderPassBounds,
140 colorTexture.get(),
141 resolveTexture.get(),
142 depthStencilTexture.get(),
143 viewport,
144 drawPasses)) {
145 return false;
146 }
147
148 if (colorTexture) {
149 this->trackCommandBufferResource(std::move(colorTexture));
150 }
151 if (resolveTexture) {
152 this->trackCommandBufferResource(std::move(resolveTexture));
153 }
154 if (depthStencilTexture) {
155 this->trackCommandBufferResource(std::move(depthStencilTexture));
156 }
157 // We just assume if you are adding a render pass that the render pass will actually do work. In
158 // theory we could have a discard load that doesn't submit any draws, clears, etc. But hopefully
159 // something so trivial would be caught before getting here.
160 SkDEBUGCODE(fHasWork = true;)
161
162 return true;
163 }
164
addComputePass(DispatchGroupSpan dispatchGroups)165 bool CommandBuffer::addComputePass(DispatchGroupSpan dispatchGroups) {
166 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
167
168 if (!this->onAddComputePass(dispatchGroups)) {
169 return false;
170 }
171
172 SkDEBUGCODE(fHasWork = true;)
173
174 return true;
175 }
176
copyBufferToBuffer(const Buffer * srcBuffer,size_t srcOffset,sk_sp<Buffer> dstBuffer,size_t dstOffset,size_t size)177 bool CommandBuffer::copyBufferToBuffer(const Buffer* srcBuffer,
178 size_t srcOffset,
179 sk_sp<Buffer> dstBuffer,
180 size_t dstOffset,
181 size_t size) {
182 SkASSERT(srcBuffer);
183 SkASSERT(dstBuffer);
184
185 if (!this->onCopyBufferToBuffer(srcBuffer, srcOffset, dstBuffer.get(), dstOffset, size)) {
186 return false;
187 }
188
189 this->trackResource(std::move(dstBuffer));
190
191 SkDEBUGCODE(fHasWork = true;)
192
193 return true;
194 }
195
copyTextureToBuffer(sk_sp<Texture> texture,SkIRect srcRect,sk_sp<Buffer> buffer,size_t bufferOffset,size_t bufferRowBytes)196 bool CommandBuffer::copyTextureToBuffer(sk_sp<Texture> texture,
197 SkIRect srcRect,
198 sk_sp<Buffer> buffer,
199 size_t bufferOffset,
200 size_t bufferRowBytes) {
201 SkASSERT(texture);
202 SkASSERT(buffer);
203
204 if (!this->onCopyTextureToBuffer(texture.get(), srcRect, buffer.get(), bufferOffset,
205 bufferRowBytes)) {
206 return false;
207 }
208
209 this->trackCommandBufferResource(std::move(texture));
210 this->trackResource(std::move(buffer));
211
212 SkDEBUGCODE(fHasWork = true;)
213
214 return true;
215 }
216
copyBufferToTexture(const Buffer * buffer,sk_sp<Texture> texture,const BufferTextureCopyData * copyData,int count)217 bool CommandBuffer::copyBufferToTexture(const Buffer* buffer,
218 sk_sp<Texture> texture,
219 const BufferTextureCopyData* copyData,
220 int count) {
221 SkASSERT(buffer);
222 SkASSERT(texture);
223 SkASSERT(count > 0 && copyData);
224
225 if (!this->onCopyBufferToTexture(buffer, texture.get(), copyData, count)) {
226 return false;
227 }
228
229 this->trackCommandBufferResource(std::move(texture));
230
231 SkDEBUGCODE(fHasWork = true;)
232
233 return true;
234 }
235
copyTextureToTexture(sk_sp<Texture> src,SkIRect srcRect,sk_sp<Texture> dst,SkIPoint dstPoint,int mipLevel)236 bool CommandBuffer::copyTextureToTexture(sk_sp<Texture> src,
237 SkIRect srcRect,
238 sk_sp<Texture> dst,
239 SkIPoint dstPoint,
240 int mipLevel) {
241 SkASSERT(src);
242 SkASSERT(dst);
243 if (src->textureInfo().isProtected() == Protected::kYes &&
244 dst->textureInfo().isProtected() != Protected::kYes) {
245 SKGPU_LOG_E("Can't copy from protected memory to non-protected");
246 return false;
247 }
248
249 if (!this->onCopyTextureToTexture(src.get(), srcRect, dst.get(), dstPoint, mipLevel)) {
250 return false;
251 }
252
253 this->trackCommandBufferResource(std::move(src));
254 this->trackCommandBufferResource(std::move(dst));
255
256 SkDEBUGCODE(fHasWork = true;)
257
258 return true;
259 }
260
synchronizeBufferToCpu(sk_sp<Buffer> buffer)261 bool CommandBuffer::synchronizeBufferToCpu(sk_sp<Buffer> buffer) {
262 SkASSERT(buffer);
263
264 bool didResultInWork = false;
265 if (!this->onSynchronizeBufferToCpu(buffer.get(), &didResultInWork)) {
266 return false;
267 }
268
269 if (didResultInWork) {
270 this->trackResource(std::move(buffer));
271 SkDEBUGCODE(fHasWork = true;)
272 }
273
274 return true;
275 }
276
clearBuffer(const Buffer * buffer,size_t offset,size_t size)277 bool CommandBuffer::clearBuffer(const Buffer* buffer, size_t offset, size_t size) {
278 SkASSERT(buffer);
279
280 if (!this->onClearBuffer(buffer, offset, size)) {
281 return false;
282 }
283
284 SkDEBUGCODE(fHasWork = true;)
285
286 return true;
287 }
288
setReplayTranslationAndClip(const SkIVector & translation,const SkIRect & clip,const SkIRect & renderTargetBounds)289 bool CommandBuffer::setReplayTranslationAndClip(const SkIVector& translation,
290 const SkIRect& clip,
291 const SkIRect& renderTargetBounds) {
292 fReplayTranslation = translation;
293 fRenderPassBounds = renderTargetBounds;
294
295 // If a replay clip is defined, we intersect it with the render target bounds.
296 if (!clip.isEmpty()) {
297 if (!fRenderPassBounds.intersect(clip.makeOffset(translation))) {
298 return false;
299 }
300 }
301
302 return true;
303 }
304
305 } // namespace skgpu::graphite
306