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