1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "VkReconstruction.h"
15
16 #include <string.h>
17
18 #include <unordered_map>
19
20 #include "FrameBuffer.h"
21 #include "VkDecoder.h"
22 #include "aemu/base/containers/EntityManager.h"
23
24 namespace gfxstream {
25 namespace vk {
26 namespace {
27
GetOpcode(const VkSnapshotApiCallInfo & info)28 uint32_t GetOpcode(const VkSnapshotApiCallInfo& info) {
29 if (info.packet.size() <= 4) return -1;
30
31 return *(reinterpret_cast<const uint32_t*>(info.packet.data()));
32 }
33
34 } // namespace
35
36 #define DEBUG_RECONSTRUCTION 0
37
38 #if DEBUG_RECONSTRUCTION
39
40 #define DEBUG_RECON(fmt, ...) INFO(fmt, ##__VA_ARGS__);
41
42 #else
43
44 #define DEBUG_RECON(fmt, ...)
45
46 #endif
47
48 VkReconstruction::VkReconstruction() = default;
49
typeTagSortedHandles(const std::vector<VkReconstruction::HandleWithState> & handles)50 std::vector<VkReconstruction::HandleWithState> typeTagSortedHandles(
51 const std::vector<VkReconstruction::HandleWithState>& handles) {
52 using EntityManagerTypeForHandles = android::base::EntityManager<32, 16, 16, int>;
53
54 std::vector<VkReconstruction::HandleWithState> res = handles;
55
56 std::sort(res.begin(), res.end(),
57 [](const VkReconstruction::HandleWithState& lhs,
58 const VkReconstruction::HandleWithState& rhs) {
59 if (lhs.second != rhs.second) {
60 return lhs.second < rhs.second;
61 }
62 return EntityManagerTypeForHandles::getHandleType(lhs.first) <
63 EntityManagerTypeForHandles::getHandleType(rhs.first);
64 });
65
66 return res;
67 }
68
clear()69 void VkReconstruction::clear() {
70 mApiCallManager.clear();
71 mHandleReconstructions.clear();
72 }
73
saveReplayBuffers(android::base::Stream * stream)74 void VkReconstruction::saveReplayBuffers(android::base::Stream* stream) {
75 DEBUG_RECON("start")
76
77 #if DEBUG_RECONSTRUCTION
78 dump();
79 #endif
80
81 std::unordered_set<uint64_t> savedApis;
82
83 std::unordered_map<HandleWithState, int, HandleWithStateHash> totalParents;
84 std::vector<HandleWithState> next;
85
86 mHandleReconstructions.forEachLiveComponent_const(
87 [&totalParents, &next](bool live, uint64_t componentHandle, uint64_t entityHandle,
88 const HandleWithStateReconstruction& item) {
89 for (int state = BEGIN; state < HANDLE_STATE_COUNT; state++) {
90 const auto& parents = item.states[state].parentHandles;
91 HandleWithState handleWithState = {entityHandle, static_cast<HandleState>(state)};
92 totalParents[handleWithState] = parents.size();
93 if (parents.empty()) {
94 next.push_back(handleWithState);
95 }
96 }
97 });
98
99 std::vector<std::vector<HandleWithState>> handlesByTopoOrder;
100
101 while (!next.empty()) {
102 next = typeTagSortedHandles(next);
103 handlesByTopoOrder.push_back(std::move(next));
104 const std::vector<HandleWithState>& current = handlesByTopoOrder.back();
105 for (const auto& handle : current) {
106 const auto& item = mHandleReconstructions.get(handle.first)->states[handle.second];
107 for (const auto& childHandle : item.childHandles) {
108 if (--totalParents[childHandle] == 0) {
109 next.push_back(childHandle);
110 }
111 }
112 }
113 }
114
115 std::vector<std::vector<uint64_t>> uniqApiRefsByTopoOrder;
116 uniqApiRefsByTopoOrder.reserve(handlesByTopoOrder.size() + 1);
117 for (const auto& handles : handlesByTopoOrder) {
118 std::vector<uint64_t> nextApis;
119 for (const auto& handle : handles) {
120 auto item = mHandleReconstructions.get(handle.first)->states[handle.second];
121 for (uint64_t apiRef : item.apiRefs) {
122 auto apiItem = mApiCallManager.get(apiRef);
123 if (!apiItem) continue;
124 if (savedApis.find(apiRef) != savedApis.end()) continue;
125 savedApis.insert(apiRef);
126 #if DEBUG_RECONSTRUCTION
127 DEBUG_RECON("adding handle 0x%lx API 0x%lx op code %d", handle.first, apiRef,
128 GetOpcode(*apiItem));
129 #endif
130 nextApis.push_back(apiRef);
131 }
132 }
133 uniqApiRefsByTopoOrder.push_back(std::move(nextApis));
134 }
135
136 uniqApiRefsByTopoOrder.push_back(getOrderedUniqueModifyApis());
137
138 size_t totalApiTraceSize = 0; // 4 bytes to store size of created handles
139
140 for (size_t i = 0; i < uniqApiRefsByTopoOrder.size(); ++i) {
141 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
142 const VkSnapshotApiCallInfo* info = mApiCallManager.get(apiHandle);
143 totalApiTraceSize += info->packet.size();
144 }
145 }
146
147 DEBUG_RECON("total api trace size: %zu", totalApiTraceSize);
148
149 std::vector<uint64_t> createdHandleBuffer;
150
151 for (size_t i = 0; i < uniqApiRefsByTopoOrder.size(); ++i) {
152 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
153 auto item = mApiCallManager.get(apiHandle);
154 for (auto createdHandle : item->createdHandles) {
155 DEBUG_RECON("save handle: 0x%lx", createdHandle);
156 createdHandleBuffer.push_back(createdHandle);
157 }
158 }
159 }
160
161 std::vector<uint8_t> apiTraceBuffer;
162 apiTraceBuffer.resize(totalApiTraceSize);
163
164 uint8_t* apiTracePtr = apiTraceBuffer.data();
165
166 for (size_t i = 0; i < uniqApiRefsByTopoOrder.size(); ++i) {
167 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
168 auto item = mApiCallManager.get(apiHandle);
169 // 4 bytes for opcode, and 4 bytes for saveBufferRaw's size field
170 DEBUG_RECON("saving api handle 0x%lx op code %d", apiHandle, GetOpcode(*item));
171 memcpy(apiTracePtr, item->packet.data(), item->packet.size());
172 apiTracePtr += item->packet.size();
173 }
174 }
175
176 DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
177 apiTraceBuffer.size());
178
179 android::base::saveBuffer(stream, createdHandleBuffer);
180 android::base::saveBuffer(stream, apiTraceBuffer);
181 }
182
183 /*static*/
loadReplayBuffers(android::base::Stream * stream,std::vector<uint64_t> * outHandleBuffer,std::vector<uint8_t> * outDecoderBuffer)184 void VkReconstruction::loadReplayBuffers(android::base::Stream* stream,
185 std::vector<uint64_t>* outHandleBuffer,
186 std::vector<uint8_t>* outDecoderBuffer) {
187 DEBUG_RECON("starting to unpack decoder replay buffer");
188
189 android::base::loadBuffer(stream, outHandleBuffer);
190 android::base::loadBuffer(stream, outDecoderBuffer);
191
192 DEBUG_RECON("finished unpacking decoder replay buffer");
193 }
194
createApiCallInfo()195 VkSnapshotApiCallInfo* VkReconstruction::createApiCallInfo() {
196 VkSnapshotApiCallHandle handle = mApiCallManager.add(VkSnapshotApiCallInfo(), 1);
197
198 auto* info = mApiCallManager.get(handle);
199 info->handle = handle;
200 return info;
201 }
202
removeHandleFromApiInfo(VkSnapshotApiCallHandle h,uint64_t toRemove)203 void VkReconstruction::removeHandleFromApiInfo(VkSnapshotApiCallHandle h, uint64_t toRemove) {
204 auto vk_item = mHandleReconstructions.get(toRemove);
205 if (!vk_item) return;
206 auto apiInfo = mApiCallManager.get(h);
207 if (!apiInfo) return;
208
209 auto& handles = apiInfo->createdHandles;
210 auto it = std::find(handles.begin(), handles.end(), toRemove);
211
212 if (it != handles.end()) {
213 handles.erase(it);
214 }
215 DEBUG_RECON("removed 1 vk handle 0x%llx from apiInfo 0x%llx, now it has %d left",
216 (unsigned long long)toRemove, (unsigned long long)h, (int)handles.size());
217 }
218
destroyApiCallInfo(VkSnapshotApiCallHandle h)219 void VkReconstruction::destroyApiCallInfo(VkSnapshotApiCallHandle h) {
220 auto item = mApiCallManager.get(h);
221
222 if (!item) return;
223
224 if (!item->createdHandles.empty()) return;
225
226 item->createdHandles.clear();
227
228 mApiCallManager.remove(h);
229 }
230
destroyApiCallInfoIfUnused(VkSnapshotApiCallInfo * info)231 void VkReconstruction::destroyApiCallInfoIfUnused(VkSnapshotApiCallInfo* info) {
232 if (!info) return;
233 auto handle = info->handle;
234 auto currentInfo = mApiCallManager.get(handle);
235 if (!currentInfo) return;
236
237 if (currentInfo->packet.empty()) {
238 mApiCallManager.remove(handle);
239 return;
240 }
241
242 if (!info->extraCreatedHandles.empty()) {
243 currentInfo->createdHandles.insert(currentInfo->createdHandles.end(), info->extraCreatedHandles.begin(),
244 info->extraCreatedHandles.end());
245 info->extraCreatedHandles.clear();
246 }
247 }
248
getApiInfo(VkSnapshotApiCallHandle h)249 VkSnapshotApiCallInfo* VkReconstruction::getApiInfo(VkSnapshotApiCallHandle h) {
250 return mApiCallManager.get(h);
251 }
252
setApiTrace(VkSnapshotApiCallInfo * apiInfo,const uint8_t * packet,size_t packetLenBytes)253 void VkReconstruction::setApiTrace(VkSnapshotApiCallInfo* apiInfo, const uint8_t* packet,
254 size_t packetLenBytes) {
255 auto* info = mApiCallManager.get(apiInfo->handle);
256 if(info) {
257 info->packet.assign(packet, packet + packetLenBytes);
258 }
259 }
260
dump()261 void VkReconstruction::dump() {
262 INFO("%s: api trace dump", __func__);
263
264 size_t traceBytesTotal = 0;
265
266 mApiCallManager.forEachLiveEntry_const(
267 [&traceBytesTotal](bool live, uint64_t handle, const VkSnapshotApiCallInfo& info) {
268 const uint32_t opcode = GetOpcode(info);
269 INFO("VkReconstruction::%s: api handle 0x%llx: %s", __func__,
270 (unsigned long long)handle, api_opcode_to_string(opcode));
271 traceBytesTotal += info.packet.size();
272 });
273
274 mHandleReconstructions.forEachLiveComponent_const(
275 [this](bool live, uint64_t componentHandle, uint64_t entityHandle,
276 const HandleWithStateReconstruction& reconstruction) {
277 INFO("VkReconstruction::%s: %p handle 0x%llx api refs:", __func__, this,
278 (unsigned long long)entityHandle);
279 for (const auto& state : reconstruction.states) {
280 for (auto apiHandle : state.apiRefs) {
281 auto apiInfo = mApiCallManager.get(apiHandle);
282 const char* apiName =
283 apiInfo ? api_opcode_to_string(GetOpcode(*apiInfo)) : "unalloced";
284 INFO("VkReconstruction::%s: 0x%llx: %s", __func__,
285 (unsigned long long)apiHandle, apiName);
286 for (auto createdHandle : apiInfo->createdHandles) {
287 INFO("VkReconstruction::%s: created 0x%llx", __func__,
288 (unsigned long long)createdHandle);
289 }
290 }
291 }
292 });
293
294 mHandleModifications.forEachLiveComponent_const([this](bool live, uint64_t componentHandle,
295 uint64_t entityHandle,
296 const HandleModification& modification) {
297 INFO("VkReconstruction::%s: mod: %p handle 0x%llx api refs:", __func__, this,
298 (unsigned long long)entityHandle);
299 for (auto apiHandle : modification.apiRefs) {
300 auto apiInfo = mApiCallManager.get(apiHandle);
301 const char* apiName = apiInfo ? api_opcode_to_string(GetOpcode(*apiInfo)) : "unalloced";
302 INFO("VkReconstruction::%s: mod: 0x%llx: %s", __func__,
303 (unsigned long long)apiHandle, apiName);
304 }
305 });
306
307 INFO("%s: total trace bytes: %zu", __func__, traceBytesTotal);
308 }
309
addHandles(const uint64_t * toAdd,uint32_t count)310 void VkReconstruction::addHandles(const uint64_t* toAdd, uint32_t count) {
311 if (!toAdd) return;
312
313 for (uint32_t i = 0; i < count; ++i) {
314 DEBUG_RECON("add 0x%llx", (unsigned long long)toAdd[i]);
315 mHandleReconstructions.add(toAdd[i], HandleWithStateReconstruction());
316 }
317 }
318
removeHandles(const uint64_t * toRemove,uint32_t count,bool recursive)319 void VkReconstruction::removeHandles(const uint64_t* toRemove, uint32_t count, bool recursive) {
320 if (!toRemove) return;
321
322 for (uint32_t i = 0; i < count; ++i) {
323 DEBUG_RECON("remove 0x%llx", (unsigned long long)toRemove[i]);
324 auto item = mHandleReconstructions.get(toRemove[i]);
325 // Delete can happen in arbitrary order.
326 // It might delete the parents before children, which will automatically remove
327 // the name.
328 if (!item) continue;
329 // Break circuler references
330 if (item->destroying) continue;
331 item->destroying = true;
332 if (!recursive) {
333 bool couldDestroy = true;
334 for (const auto& state : item->states) {
335 if (!state.childHandles.size()) {
336 continue;
337 }
338 couldDestroy = false;
339 break;
340 }
341 // TODO(b/330769702): perform delayed destroy when all children are destroyed.
342 if (couldDestroy) {
343 forEachHandleDeleteApi(toRemove + i, 1);
344 mHandleReconstructions.remove(toRemove[i]);
345 } else {
346 DEBUG_RECON("delay destroy of 0x%lx, TODO: actually destroy it", toRemove[i]);
347 item->delayed_destroy = true;
348 item->destroying = false;
349 }
350 continue;
351 }
352 for (size_t j = 0; j < item->states.size(); j++) {
353 for (const auto& parentHandle : item->states[j].parentHandles) {
354 auto parentItem = mHandleReconstructions.get(parentHandle.first);
355 if (!parentItem) {
356 continue;
357 }
358 parentItem->states[parentHandle.second].childHandles.erase(
359 {toRemove[i], static_cast<HandleState>(j)});
360 }
361 item->states[j].parentHandles.clear();
362 std::vector<uint64_t> childHandles;
363 for (const auto& childHandle : item->states[j].childHandles) {
364 if (childHandle.second == CREATED) {
365 childHandles.push_back(childHandle.first);
366 }
367 }
368 item->states[j].childHandles.clear();
369 removeHandles(childHandles.data(), childHandles.size());
370 }
371 forEachHandleDeleteApi(toRemove + i, 1);
372 mHandleReconstructions.remove(toRemove[i]);
373 }
374 }
375
forEachHandleAddApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle,HandleState state)376 void VkReconstruction::forEachHandleAddApi(const uint64_t* toProcess, uint32_t count,
377 uint64_t apiHandle, HandleState state) {
378 if (!toProcess) return;
379
380 for (uint32_t i = 0; i < count; ++i) {
381 auto item = mHandleReconstructions.get(toProcess[i]);
382 if (!item) continue;
383
384 item->states[state].apiRefs.push_back(apiHandle);
385 DEBUG_RECON("handle 0x%lx state %d added api 0x%lx", toProcess[i], state, apiHandle);
386 }
387 }
388
forEachHandleDeleteApi(const uint64_t * toProcess,uint32_t count)389 void VkReconstruction::forEachHandleDeleteApi(const uint64_t* toProcess, uint32_t count) {
390 if (!toProcess) return;
391
392 for (uint32_t i = 0; i < count; ++i) {
393 DEBUG_RECON("deleting api for 0x%lx", toProcess[i]);
394 auto item = mHandleReconstructions.get(toProcess[i]);
395
396 if (!item) continue;
397
398 for (auto& state : item->states) {
399 for (auto handle : state.apiRefs) {
400 removeHandleFromApiInfo(handle, toProcess[i]);
401 destroyApiCallInfo(handle);
402 }
403 state.apiRefs.clear();
404 }
405
406 auto modifyItem = mHandleModifications.get(toProcess[i]);
407
408 if (!modifyItem) continue;
409
410 modifyItem->apiRefs.clear();
411 }
412 }
413
addHandleDependency(const uint64_t * handles,uint32_t count,uint64_t parentHandle,HandleState childState,HandleState parentState)414 void VkReconstruction::addHandleDependency(const uint64_t* handles, uint32_t count,
415 uint64_t parentHandle, HandleState childState,
416 HandleState parentState) {
417 if (!handles) return;
418
419 if (!parentHandle) return;
420
421 auto parentItem = mHandleReconstructions.get(parentHandle);
422
423 if (!parentItem) {
424 DEBUG_RECON("WARN: adding null parent item: 0x%lx", parentHandle);
425 return;
426 }
427 auto& parentItemState = parentItem->states[parentState];
428
429 for (uint32_t i = 0; i < count; ++i) {
430 auto childItem = mHandleReconstructions.get(handles[i]);
431 if (!childItem) {
432 continue;
433 }
434 parentItemState.childHandles.insert({handles[i], static_cast<HandleState>(childState)});
435 childItem->states[childState].parentHandles.push_back(
436 {parentHandle, static_cast<HandleState>(parentState)});
437 DEBUG_RECON("Child handle 0x%lx state %d depends on parent handle 0x%lx state %d",
438 handles[i], childState, parentHandle, parentState);
439 }
440 }
441
setCreatedHandlesForApi(uint64_t apiHandle,const uint64_t * created,uint32_t count)442 void VkReconstruction::setCreatedHandlesForApi(uint64_t apiHandle, const uint64_t* created,
443 uint32_t count) {
444 if (!created) return;
445
446 auto item = mApiCallManager.get(apiHandle);
447
448 if (!item) return;
449
450 item->createdHandles.insert(item->createdHandles.end(), created, created + count);
451 }
452
forEachHandleAddModifyApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle)453 void VkReconstruction::forEachHandleAddModifyApi(const uint64_t* toProcess, uint32_t count,
454 uint64_t apiHandle) {
455 if (!toProcess) return;
456
457 for (uint32_t i = 0; i < count; ++i) {
458 auto item = mHandleModifications.get(toProcess[i]);
459 if (!item) {
460 mHandleModifications.add(toProcess[i], HandleModification());
461 item = mHandleModifications.get(toProcess[i]);
462 }
463
464 if (!item) continue;
465
466 item->apiRefs.push_back(apiHandle);
467 }
468 }
469
forEachHandleClearModifyApi(const uint64_t * toProcess,uint32_t count)470 void VkReconstruction::forEachHandleClearModifyApi(const uint64_t* toProcess, uint32_t count) {
471 if (!toProcess) return;
472
473 for (uint32_t i = 0; i < count; ++i) {
474 auto item = mHandleModifications.get(toProcess[i]);
475
476 if (!item) continue;
477
478 item->apiRefs.clear();
479 }
480 }
481
getOrderedUniqueModifyApis() const482 std::vector<uint64_t> VkReconstruction::getOrderedUniqueModifyApis() const {
483 std::vector<HandleModification> orderedModifies;
484
485 // Now add all handle modifications to the trace, ordered by the .order field.
486 mHandleModifications.forEachLiveComponent_const(
487 [&orderedModifies](bool live, uint64_t componentHandle, uint64_t entityHandle,
488 const HandleModification& mod) { orderedModifies.push_back(mod); });
489
490 // Sort by the |order| field for each modify API
491 // since it may be important to apply modifies in a particular
492 // order (e.g., when dealing with descriptor set updates
493 // or commands in a command buffer).
494 std::sort(orderedModifies.begin(), orderedModifies.end(),
495 [](const HandleModification& lhs, const HandleModification& rhs) {
496 return lhs.order < rhs.order;
497 });
498
499 std::unordered_set<uint64_t> usedModifyApis;
500 std::vector<uint64_t> orderedUniqueModifyApis;
501
502 for (const auto& mod : orderedModifies) {
503 for (auto apiRef : mod.apiRefs) {
504 if (usedModifyApis.find(apiRef) == usedModifyApis.end()) {
505 orderedUniqueModifyApis.push_back(apiRef);
506 usedModifyApis.insert(apiRef);
507 }
508 }
509 }
510
511 return orderedUniqueModifyApis;
512 }
513
514 } // namespace vk
515 } // namespace gfxstream
516