1 /*
2 * Copyright 2022 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/QueueManager.h"
9
10 #include "include/gpu/graphite/Recording.h"
11 #include "src/gpu/RefCntedCallback.h"
12 #include "src/gpu/graphite/CommandBuffer.h"
13 #include "src/gpu/graphite/ContextPriv.h"
14 #include "src/gpu/graphite/GpuWorkSubmission.h"
15 #include "src/gpu/graphite/Log.h"
16 #include "src/gpu/graphite/RecordingPriv.h"
17 #include "src/gpu/graphite/Surface_Graphite.h"
18 #include "src/gpu/graphite/Task.h"
19
20 namespace skgpu::graphite {
21
22 // This constant determines how many OutstandingSubmissions are allocated together as a block in
23 // the deque. As such it needs to balance allocating too much memory vs. incurring
24 // allocation/deallocation thrashing. It should roughly correspond to the max number of outstanding
25 // submissions we expect to see.
26 static constexpr int kDefaultOutstandingAllocCnt = 8;
27
QueueManager(const SharedContext * sharedContext)28 QueueManager::QueueManager(const SharedContext* sharedContext)
29 : fSharedContext(sharedContext)
30 , fOutstandingSubmissions(sizeof(OutstandingSubmission), kDefaultOutstandingAllocCnt) {
31 }
32
~QueueManager()33 QueueManager::~QueueManager() {
34 this->checkForFinishedWork(SyncToCpu::kYes);
35 }
36
setupCommandBuffer(ResourceProvider * resourceProvider)37 bool QueueManager::setupCommandBuffer(ResourceProvider* resourceProvider) {
38 if (!fCurrentCommandBuffer) {
39 if (fAvailableCommandBuffers.size()) {
40 fCurrentCommandBuffer = std::move(fAvailableCommandBuffers.back());
41 fAvailableCommandBuffers.pop_back();
42 if (!fCurrentCommandBuffer->setNewCommandBufferResources()) {
43 fCurrentCommandBuffer.reset();
44 }
45 }
46 }
47 if (!fCurrentCommandBuffer) {
48 fCurrentCommandBuffer = this->getNewCommandBuffer(resourceProvider);
49 }
50 if (!fCurrentCommandBuffer) {
51 return false;
52 }
53
54 return true;
55 }
56
addRecording(const InsertRecordingInfo & info,Context * context)57 bool QueueManager::addRecording(const InsertRecordingInfo& info, Context* context) {
58 sk_sp<RefCntedCallback> callback;
59 if (info.fFinishedProc) {
60 callback = RefCntedCallback::Make(info.fFinishedProc, info.fFinishedContext);
61 }
62
63 SkASSERT(info.fRecording);
64 if (!info.fRecording) {
65 if (callback) {
66 callback->setFailureResult();
67 }
68 SKGPU_LOG_E("No valid Recording passed into addRecording call");
69 return false;
70 }
71
72 if (info.fTargetSurface &&
73 !static_cast<const SkSurface_Base*>(info.fTargetSurface)->isGraphiteBacked()) {
74 if (callback) {
75 callback->setFailureResult();
76 }
77 SKGPU_LOG_E("Target surface passed into addRecording call is not graphite-backed");
78 return false;
79 }
80
81 auto resourceProvider = context->priv().resourceProvider();
82 if (!this->setupCommandBuffer(resourceProvider)) {
83 if (callback) {
84 callback->setFailureResult();
85 }
86 SKGPU_LOG_E("CommandBuffer creation failed");
87 return false;
88 }
89
90 if (info.fRecording->priv().hasNonVolatileLazyProxies()) {
91 if (!info.fRecording->priv().instantiateNonVolatileLazyProxies(resourceProvider)) {
92 if (callback) {
93 callback->setFailureResult();
94 }
95 SKGPU_LOG_E("Non-volatile PromiseImage instantiation has failed");
96 return false;
97 }
98 }
99
100 if (info.fRecording->priv().hasVolatileLazyProxies()) {
101 if (!info.fRecording->priv().instantiateVolatileLazyProxies(resourceProvider)) {
102 if (callback) {
103 callback->setFailureResult();
104 }
105 info.fRecording->priv().deinstantiateVolatileLazyProxies();
106 SKGPU_LOG_E("Volatile PromiseImage instantiation has failed");
107 return false;
108 }
109 }
110
111 if (!info.fRecording->priv().addCommands(context,
112 fCurrentCommandBuffer.get(),
113 static_cast<Surface*>(info.fTargetSurface),
114 info.fTargetTranslation)) {
115 if (callback) {
116 callback->setFailureResult();
117 }
118 info.fRecording->priv().deinstantiateVolatileLazyProxies();
119 SKGPU_LOG_E("Adding Recording commands to the CommandBuffer has failed");
120 return false;
121 }
122
123 if (callback) {
124 fCurrentCommandBuffer->addFinishedProc(std::move(callback));
125 }
126
127 info.fRecording->priv().deinstantiateVolatileLazyProxies();
128 return true;
129 }
130
addTask(Task * task,Context * context)131 bool QueueManager::addTask(Task* task,
132 Context* context) {
133 SkASSERT(task);
134 if (!task) {
135 SKGPU_LOG_E("No valid Task passed into addTask call");
136 return false;
137 }
138
139 if (!this->setupCommandBuffer(context->priv().resourceProvider())) {
140 SKGPU_LOG_E("CommandBuffer creation failed");
141 return false;
142 }
143
144 if (!task->addCommands(context, fCurrentCommandBuffer.get(), {})) {
145 SKGPU_LOG_E("Adding Task commands to the CommandBuffer has failed");
146 return false;
147 }
148
149 return true;
150 }
151
addFinishInfo(const InsertFinishInfo & info,ResourceProvider * resourceProvider)152 bool QueueManager::addFinishInfo(const InsertFinishInfo& info,
153 ResourceProvider* resourceProvider) {
154 sk_sp<RefCntedCallback> callback;
155 if (info.fFinishedProc) {
156 callback = RefCntedCallback::Make(info.fFinishedProc, info.fFinishedContext);
157 }
158
159 if (!this->setupCommandBuffer(resourceProvider)) {
160 if (callback) {
161 callback->setFailureResult();
162 }
163 SKGPU_LOG_E("CommandBuffer creation failed");
164 return false;
165 }
166
167 if (callback) {
168 fCurrentCommandBuffer->addFinishedProc(std::move(callback));
169 }
170
171 return true;
172 }
173
submitToGpu()174 bool QueueManager::submitToGpu() {
175 if (!fCurrentCommandBuffer) {
176 // We warn because this probably representative of a bad client state, where they don't
177 // need to submit but didn't notice, but technically the submit itself is fine (no-op), so
178 // we return true.
179 SKGPU_LOG_W("Submit called with no active command buffer!");
180 return true;
181 }
182
183 #ifdef SK_DEBUG
184 if (!fCurrentCommandBuffer->hasWork()) {
185 SKGPU_LOG_W("Submitting empty command buffer!");
186 }
187 #endif
188
189 auto submission = this->onSubmitToGpu();
190 if (!submission) {
191 return false;
192 }
193
194 new (fOutstandingSubmissions.push_back()) OutstandingSubmission(std::move(submission));
195 return true;
196 }
197
checkForFinishedWork(SyncToCpu sync)198 void QueueManager::checkForFinishedWork(SyncToCpu sync) {
199 if (sync == SyncToCpu::kYes) {
200 // wait for the last submission to finish
201 OutstandingSubmission* back = (OutstandingSubmission*)fOutstandingSubmissions.back();
202 if (back) {
203 (*back)->waitUntilFinished();
204 }
205 }
206
207 // Iterate over all the outstanding submissions to see if any have finished. The work
208 // submissions are in order from oldest to newest, so we start at the front to check if they
209 // have finished. If so we pop it off and move onto the next.
210 // Repeat till we find a submission that has not finished yet (and all others afterwards are
211 // also guaranteed to not have finished).
212 OutstandingSubmission* front = (OutstandingSubmission*)fOutstandingSubmissions.front();
213 while (front && (*front)->isFinished()) {
214 // Make sure we remove before deleting as deletion might try to kick off another submit
215 // (though hopefully *not* in Graphite).
216 fOutstandingSubmissions.pop_front();
217 // Since we used placement new we are responsible for calling the destructor manually.
218 front->~OutstandingSubmission();
219 front = (OutstandingSubmission*)fOutstandingSubmissions.front();
220 }
221 SkASSERT(sync == SyncToCpu::kNo || fOutstandingSubmissions.empty());
222 }
223
returnCommandBuffer(std::unique_ptr<CommandBuffer> commandBuffer)224 void QueueManager::returnCommandBuffer(std::unique_ptr<CommandBuffer> commandBuffer) {
225 fAvailableCommandBuffers.push_back(std::move(commandBuffer));
226 }
227
228 } // namespace skgpu::graphite
229