• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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