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 "render-utils/IOStream.h"
22 #include "VkDecoder.h"
23 #include "aemu/base/containers/EntityManager.h"
24
25 namespace gfxstream {
26 namespace vk {
27
28 #define DEBUG_RECONSTRUCTION 0
29
30 #if DEBUG_RECONSTRUCTION
31
32 #define DEBUG_RECON(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
33
34 #else
35
36 #define DEBUG_RECON(fmt, ...)
37
38 #endif
39
40 VkReconstruction::VkReconstruction() = default;
41
typeTagSortedHandles(const std::vector<uint64_t> & handles)42 std::vector<uint64_t> typeTagSortedHandles(const std::vector<uint64_t>& handles) {
43 using EntityManagerTypeForHandles = android::base::EntityManager<32, 16, 16, int>;
44
45 std::vector<uint64_t> res = handles;
46
47 std::sort(res.begin(), res.end(), [](uint64_t lhs, uint64_t rhs) {
48 return EntityManagerTypeForHandles::getHandleType(lhs) <
49 EntityManagerTypeForHandles::getHandleType(rhs);
50 });
51
52 return res;
53 }
54
save(android::base::Stream * stream)55 void VkReconstruction::save(android::base::Stream* stream) {
56 DEBUG_RECON("start")
57
58 #if DEBUG_RECON
59 dump();
60 #endif
61
62 std::unordered_map<uint64_t, uint64_t> backDeps;
63
64 mHandleReconstructions.forEachLiveComponent_const(
65 [&backDeps](bool live, uint64_t componentHandle, uint64_t entityHandle,
66 const HandleReconstruction& item) {
67 for (auto handle : item.childHandles) {
68 backDeps[handle] = entityHandle;
69 }
70 });
71
72 std::vector<uint64_t> topoOrder;
73
74 mHandleReconstructions.forEachLiveComponent_const(
75 [&topoOrder, &backDeps](bool live, uint64_t componentHandle, uint64_t entityHandle,
76 const HandleReconstruction& item) {
77 // Start with populating the roots
78 if (backDeps.find(entityHandle) == backDeps.end()) {
79 DEBUG_RECON("found root: 0x%llx", (unsigned long long)entityHandle);
80 topoOrder.push_back(entityHandle);
81 }
82 });
83
84 std::vector<uint64_t> next;
85
86 std::unordered_map<uint64_t, uint64_t> uniqApiRefsToTopoOrder;
87 std::unordered_map<uint64_t, std::vector<uint64_t>> uniqApiRefsByTopoOrder;
88 std::unordered_map<uint64_t, std::vector<uint64_t>> uniqApiRefsByTopoAndDependencyOrder;
89
90 size_t topoLevel = 0;
91
92 topoOrder = typeTagSortedHandles(topoOrder);
93
94 while (!topoOrder.empty()) {
95 next.clear();
96
97 for (auto handle : topoOrder) {
98 auto item = mHandleReconstructions.get(handle);
99 // item could have been deleted.
100 if (!item) {
101 continue;
102 }
103
104 for (auto apiHandle : item->apiRefs) {
105 if (uniqApiRefsToTopoOrder.find(apiHandle) == uniqApiRefsToTopoOrder.end()) {
106 DEBUG_RECON("level %zu: 0x%llx api ref: 0x%llx", topoLevel,
107 (unsigned long long)handle, (unsigned long long)apiHandle);
108 auto& refs = uniqApiRefsByTopoOrder[topoLevel];
109 refs.push_back(apiHandle);
110 }
111
112 uniqApiRefsToTopoOrder[apiHandle] = topoLevel;
113 }
114
115 for (auto childHandle : item->childHandles) {
116 next.push_back(childHandle);
117 }
118 }
119
120 next = typeTagSortedHandles(next);
121
122 topoOrder = next;
123 ++topoLevel;
124 }
125
126 uniqApiRefsByTopoOrder[topoLevel] = getOrderedUniqueModifyApis();
127 ++topoLevel;
128
129 size_t totalApiTraceSize = 0; // 4 bytes to store size of created handles
130
131 for (size_t i = 0; i < topoLevel; ++i) {
132 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
133 auto item = mApiTrace.get(apiHandle);
134 totalApiTraceSize += 4; // opcode
135 totalApiTraceSize += 4; // buffer size of trace
136 totalApiTraceSize += item->traceBytes; // the actual trace
137 }
138 }
139
140 DEBUG_RECON("total api trace size: %zu", totalApiTraceSize);
141
142 std::vector<uint64_t> createdHandleBuffer;
143
144 for (size_t i = 0; i < topoLevel; ++i) {
145 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
146 auto item = mApiTrace.get(apiHandle);
147 for (auto createdHandle : item->createdHandles) {
148 DEBUG_RECON("save handle: 0x%llx\n", createdHandle);
149 createdHandleBuffer.push_back(createdHandle);
150 }
151 }
152 }
153
154 std::vector<uint8_t> apiTraceBuffer;
155 apiTraceBuffer.resize(totalApiTraceSize);
156
157 uint8_t* apiTracePtr = apiTraceBuffer.data();
158
159 for (size_t i = 0; i < topoLevel; ++i) {
160 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
161 auto item = mApiTrace.get(apiHandle);
162 // 4 bytes for opcode, and 4 bytes for saveBufferRaw's size field
163 memcpy(apiTracePtr, &item->opCode, sizeof(uint32_t));
164 apiTracePtr += 4;
165 uint32_t traceBytesForSnapshot = item->traceBytes + 8;
166 memcpy(apiTracePtr, &traceBytesForSnapshot,
167 sizeof(uint32_t)); // and 8 bytes for 'self' struct of { opcode, packetlen } as
168 // that is what decoder expects
169 apiTracePtr += 4;
170 memcpy(apiTracePtr, item->trace.data(), item->traceBytes);
171 apiTracePtr += item->traceBytes;
172 }
173 }
174
175 DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
176 apiTraceBuffer.size());
177
178 android::base::saveBufferRaw(stream, (char*)(createdHandleBuffer.data()),
179 createdHandleBuffer.size() * sizeof(uint64_t));
180 android::base::saveBufferRaw(stream, (char*)(apiTraceBuffer.data()), apiTraceBuffer.size());
181 }
182
183 class TrivialStream : public IOStream {
184 public:
TrivialStream()185 TrivialStream() : IOStream(4) {}
186 virtual ~TrivialStream() = default;
187
allocBuffer(size_t minSize)188 void* allocBuffer(size_t minSize) {
189 size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
190 if (!m_buf) {
191 m_buf = (unsigned char*)malloc(allocSize);
192 } else if (m_bufsize < allocSize) {
193 unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
194 if (p != NULL) {
195 m_buf = p;
196 m_bufsize = allocSize;
197 } else {
198 ERR("realloc (%zu) failed\n", allocSize);
199 free(m_buf);
200 m_buf = NULL;
201 m_bufsize = 0;
202 }
203 }
204
205 return m_buf;
206 }
207
commitBuffer(size_t size)208 int commitBuffer(size_t size) {
209 if (size == 0) return 0;
210 return writeFully(m_buf, size);
211 }
212
writeFully(const void * buf,size_t len)213 int writeFully(const void* buf, size_t len) { return 0; }
214
readFully(void * buf,size_t len)215 const unsigned char* readFully(void* buf, size_t len) { return NULL; }
216
getDmaForReading(uint64_t guest_paddr)217 virtual void* getDmaForReading(uint64_t guest_paddr) { return nullptr; }
unlockDma(uint64_t guest_paddr)218 virtual void unlockDma(uint64_t guest_paddr) {}
219
220 protected:
readRaw(void * buf,size_t * inout_len)221 virtual const unsigned char* readRaw(void* buf, size_t* inout_len) { return nullptr; }
onSave(android::base::Stream * stream)222 virtual void onSave(android::base::Stream* stream) {}
onLoad(android::base::Stream * stream)223 virtual unsigned char* onLoad(android::base::Stream* stream) { return nullptr; }
224 };
225
load(android::base::Stream * stream,emugl::GfxApiLogger & gfxLogger,emugl::HealthMonitor<> * healthMonitor)226 void VkReconstruction::load(android::base::Stream* stream, emugl::GfxApiLogger& gfxLogger,
227 emugl::HealthMonitor<>* healthMonitor) {
228 DEBUG_RECON("start. assuming VkDecoderGlobalState has been cleared for loading already");
229 mApiTrace.clear();
230 mHandleReconstructions.clear();
231
232 std::vector<uint8_t> createdHandleBuffer;
233 std::vector<uint8_t> apiTraceBuffer;
234
235 android::base::loadBuffer(stream, &createdHandleBuffer);
236 android::base::loadBuffer(stream, &apiTraceBuffer);
237
238 DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
239 apiTraceBuffer.size());
240
241 uint32_t createdHandleBufferSize = createdHandleBuffer.size();
242
243 mLoadedTrace.resize(4 + createdHandleBufferSize + apiTraceBuffer.size());
244
245 unsigned char* finalTraceData = (unsigned char*)(mLoadedTrace.data());
246
247 memcpy(finalTraceData, &createdHandleBufferSize, sizeof(uint32_t));
248 memcpy(finalTraceData + 4, createdHandleBuffer.data(), createdHandleBufferSize);
249 memcpy(finalTraceData + 4 + createdHandleBufferSize, apiTraceBuffer.data(),
250 apiTraceBuffer.size());
251
252 VkDecoder decoderForLoading;
253 // A decoder that is set for snapshot load will load up the created handles first,
254 // if any, allowing us to 'catch' the results as they are decoded.
255 decoderForLoading.setForSnapshotLoad(true);
256 TrivialStream trivialStream;
257
258 DEBUG_RECON("start decoding trace");
259
260 // TODO: This needs to be the puid seqno ptr
261 auto resources = ProcessResources::create();
262 VkDecoderContext context = {
263 .processName = nullptr,
264 .gfxApiLogger = &gfxLogger,
265 .healthMonitor = healthMonitor,
266 };
267 decoderForLoading.decode(mLoadedTrace.data(), mLoadedTrace.size(), &trivialStream, resources.get(),
268 context);
269
270 DEBUG_RECON("finished decoding trace");
271 }
272
createApiInfo()273 VkReconstruction::ApiHandle VkReconstruction::createApiInfo() {
274 auto handle = mApiTrace.add(ApiInfo(), 1);
275 return handle;
276 }
277
destroyApiInfo(VkReconstruction::ApiHandle h)278 void VkReconstruction::destroyApiInfo(VkReconstruction::ApiHandle h) {
279 auto item = mApiTrace.get(h);
280
281 if (!item) return;
282
283 item->traceBytes = 0;
284 item->createdHandles.clear();
285
286 mApiTrace.remove(h);
287 }
288
getApiInfo(VkReconstruction::ApiHandle h)289 VkReconstruction::ApiInfo* VkReconstruction::getApiInfo(VkReconstruction::ApiHandle h) {
290 return mApiTrace.get(h);
291 }
292
setApiTrace(VkReconstruction::ApiInfo * apiInfo,uint32_t opCode,const uint8_t * traceBegin,size_t traceBytes)293 void VkReconstruction::setApiTrace(VkReconstruction::ApiInfo* apiInfo, uint32_t opCode,
294 const uint8_t* traceBegin, size_t traceBytes) {
295 if (apiInfo->trace.size() < traceBytes) apiInfo->trace.resize(traceBytes);
296 apiInfo->opCode = opCode;
297 memcpy(apiInfo->trace.data(), traceBegin, traceBytes);
298 apiInfo->traceBytes = traceBytes;
299 }
300
dump()301 void VkReconstruction::dump() {
302 fprintf(stderr, "%s: api trace dump\n", __func__);
303
304 size_t traceBytesTotal = 0;
305
306 mApiTrace.forEachLiveEntry_const(
307 [&traceBytesTotal](bool live, uint64_t handle, const ApiInfo& info) {
308 fprintf(stderr, "VkReconstruction::%s: api handle 0x%llx: %s\n", __func__,
309 (unsigned long long)handle, api_opcode_to_string(info.opCode));
310 traceBytesTotal += info.traceBytes;
311 });
312
313 mHandleReconstructions.forEachLiveComponent_const(
314 [this](bool live, uint64_t componentHandle, uint64_t entityHandle,
315 const HandleReconstruction& reconstruction) {
316 fprintf(stderr, "VkReconstruction::%s: %p handle 0x%llx api refs:\n", __func__, this,
317 (unsigned long long)entityHandle);
318 for (auto apiHandle : reconstruction.apiRefs) {
319 auto apiInfo = mApiTrace.get(apiHandle);
320 const char* apiName = apiInfo ? api_opcode_to_string(apiInfo->opCode) : "unalloced";
321 fprintf(stderr, "VkReconstruction::%s: 0x%llx: %s\n", __func__,
322 (unsigned long long)apiHandle, apiName);
323 for (auto createdHandle : apiInfo->createdHandles) {
324 fprintf(stderr, "VkReconstruction::%s: created 0x%llx\n", __func__,
325 (unsigned long long)createdHandle);
326 }
327 }
328 });
329
330 mHandleModifications.forEachLiveComponent_const([this](bool live, uint64_t componentHandle,
331 uint64_t entityHandle,
332 const HandleModification& modification) {
333 fprintf(stderr, "VkReconstruction::%s: mod: %p handle 0x%llx api refs:\n", __func__, this,
334 (unsigned long long)entityHandle);
335 for (auto apiHandle : modification.apiRefs) {
336 auto apiInfo = mApiTrace.get(apiHandle);
337 const char* apiName = apiInfo ? api_opcode_to_string(apiInfo->opCode) : "unalloced";
338 fprintf(stderr, "VkReconstruction::%s: mod: 0x%llx: %s\n", __func__,
339 (unsigned long long)apiHandle, apiName);
340 }
341 });
342
343 fprintf(stderr, "%s: total trace bytes: %zu\n", __func__, traceBytesTotal);
344 }
345
addHandles(const uint64_t * toAdd,uint32_t count)346 void VkReconstruction::addHandles(const uint64_t* toAdd, uint32_t count) {
347 if (!toAdd) return;
348
349 for (uint32_t i = 0; i < count; ++i) {
350 DEBUG_RECON("add 0x%llx", (unsigned long long)toAdd[i]);
351 mHandleReconstructions.add(toAdd[i], HandleReconstruction());
352 }
353 }
354
removeHandles(const uint64_t * toRemove,uint32_t count)355 void VkReconstruction::removeHandles(const uint64_t* toRemove, uint32_t count) {
356 if (!toRemove) return;
357
358 forEachHandleDeleteApi(toRemove, count);
359
360 for (uint32_t i = 0; i < count; ++i) {
361 DEBUG_RECON("remove 0x%llx", (unsigned long long)toRemove[i]);
362 auto item = mHandleReconstructions.get(toRemove[i]);
363
364 if (!item) continue;
365
366 mHandleReconstructions.remove(toRemove[i]);
367
368 removeHandles(item->childHandles.data(), item->childHandles.size());
369
370 item->childHandles.clear();
371 }
372 }
373
forEachHandleAddApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle)374 void VkReconstruction::forEachHandleAddApi(const uint64_t* toProcess, uint32_t count,
375 uint64_t apiHandle) {
376 if (!toProcess) return;
377
378 for (uint32_t i = 0; i < count; ++i) {
379 auto item = mHandleReconstructions.get(toProcess[i]);
380
381 if (!item) continue;
382
383 item->apiRefs.push_back(apiHandle);
384 }
385 }
386
forEachHandleDeleteApi(const uint64_t * toProcess,uint32_t count)387 void VkReconstruction::forEachHandleDeleteApi(const uint64_t* toProcess, uint32_t count) {
388 if (!toProcess) return;
389
390 for (uint32_t i = 0; i < count; ++i) {
391 auto item = mHandleReconstructions.get(toProcess[i]);
392
393 if (!item) continue;
394
395 for (auto handle : item->apiRefs) {
396 destroyApiInfo(handle);
397 }
398
399 item->apiRefs.clear();
400
401 auto modifyItem = mHandleModifications.get(toProcess[i]);
402
403 if (!modifyItem) continue;
404
405 modifyItem->apiRefs.clear();
406 }
407 }
408
addHandleDependency(const uint64_t * handles,uint32_t count,uint64_t parentHandle)409 void VkReconstruction::addHandleDependency(const uint64_t* handles, uint32_t count,
410 uint64_t parentHandle) {
411 if (!handles) return;
412
413 auto item = mHandleReconstructions.get(parentHandle);
414
415 if (!item) return;
416
417 for (uint32_t i = 0; i < count; ++i) {
418 item->childHandles.push_back(handles[i]);
419 }
420 }
421
setCreatedHandlesForApi(uint64_t apiHandle,const uint64_t * created,uint32_t count)422 void VkReconstruction::setCreatedHandlesForApi(uint64_t apiHandle, const uint64_t* created,
423 uint32_t count) {
424 if (!created) return;
425
426 auto item = mApiTrace.get(apiHandle);
427
428 if (!item) return;
429
430 for (uint32_t i = 0; i < count; ++i) {
431 item->createdHandles.push_back(created[i]);
432 }
433 }
434
forEachHandleAddModifyApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle)435 void VkReconstruction::forEachHandleAddModifyApi(const uint64_t* toProcess, uint32_t count,
436 uint64_t apiHandle) {
437 if (!toProcess) return;
438
439 for (uint32_t i = 0; i < count; ++i) {
440 mHandleModifications.add(toProcess[i], HandleModification());
441
442 auto item = mHandleModifications.get(toProcess[i]);
443
444 if (!item) continue;
445
446 item->apiRefs.push_back(apiHandle);
447 }
448 }
449
getOrderedUniqueModifyApis() const450 std::vector<uint64_t> VkReconstruction::getOrderedUniqueModifyApis() const {
451 std::vector<HandleModification> orderedModifies;
452
453 // Now add all handle modifications to the trace, ordered by the .order field.
454 mHandleModifications.forEachLiveComponent_const(
455 [&orderedModifies](bool live, uint64_t componentHandle, uint64_t entityHandle,
456 const HandleModification& mod) { orderedModifies.push_back(mod); });
457
458 // Sort by the |order| field for each modify API
459 // since it may be important to apply modifies in a particular
460 // order (e.g., when dealing with descriptor set updates
461 // or commands in a command buffer).
462 std::sort(orderedModifies.begin(), orderedModifies.end(),
463 [](const HandleModification& lhs, const HandleModification& rhs) {
464 return lhs.order < rhs.order;
465 });
466
467 std::unordered_set<uint64_t> usedModifyApis;
468 std::vector<uint64_t> orderedUniqueModifyApis;
469
470 for (const auto& mod : orderedModifies) {
471 for (auto apiRef : mod.apiRefs) {
472 if (usedModifyApis.find(apiRef) == usedModifyApis.end()) {
473 orderedUniqueModifyApis.push_back(apiRef);
474 usedModifyApis.insert(apiRef);
475 }
476 }
477 }
478
479 return orderedUniqueModifyApis;
480 }
481
482 } // namespace vk
483 } // namespace gfxstream
484