• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2024 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 
15 #include "VirtioGpuFrontend.h"
16 
17 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
18 #include <filesystem>
19 #include <fcntl.h>
20 // X11 defines status as a preprocessor define which messes up
21 // anyone with a `Status` type.
22 #include <google/protobuf/io/zero_copy_stream_impl.h>
23 #include <google/protobuf/text_format.h>
24 #endif  // ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
25 
26 #include <vulkan/vulkan.h>
27 
28 #include "FrameBuffer.h"
29 #include "FrameworkFormats.h"
30 #include "VkCommonOperations.h"
31 #include "aemu/base/files/StdioStream.h"
32 #include "aemu/base/memory/SharedMemory.h"
33 #include "aemu/base/threads/WorkerThread.h"
34 #include "gfxstream/host/Tracing.h"
35 #include "host-common/AddressSpaceService.h"
36 #include "host-common/address_space_device.h"
37 #include "host-common/address_space_device.hpp"
38 #include "host-common/address_space_device_control_ops.h"
39 #include "host-common/opengles.h"
40 #include "virtgpu_gfxstream_protocol.h"
41 
42 namespace gfxstream {
43 namespace host {
44 namespace {
45 
46 using android::base::DescriptorType;
47 using android::base::SharedMemory;
48 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
49 using gfxstream::host::snapshot::VirtioGpuContextSnapshot;
50 using gfxstream::host::snapshot::VirtioGpuFrontendSnapshot;
51 using gfxstream::host::snapshot::VirtioGpuResourceSnapshot;
52 #endif  // ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
53 
54 struct VirtioGpuCmd {
55     uint32_t op;
56     uint32_t cmdSize;
57     unsigned char buf[0];
58 } __attribute__((packed));
59 
convert32to64(uint32_t lo,uint32_t hi)60 static uint64_t convert32to64(uint32_t lo, uint32_t hi) {
61     return ((uint64_t)lo) | (((uint64_t)hi) << 32);
62 }
63 
64 }  // namespace
65 
66 class CleanupThread {
67    public:
68     using GenericCleanup = std::function<void()>;
69 
CleanupThread()70     CleanupThread()
71         : mWorker([](CleanupTask task) {
72               return std::visit(
73                   [](auto&& work) {
74                       using T = std::decay_t<decltype(work)>;
75                       if constexpr (std::is_same_v<T, GenericCleanup>) {
76                           work();
77                           return android::base::WorkerProcessingResult::Continue;
78                       } else if constexpr (std::is_same_v<T, Exit>) {
79                           return android::base::WorkerProcessingResult::Stop;
80                       }
81                   },
82                   std::move(task));
83           }) {
84         mWorker.start();
85     }
86 
~CleanupThread()87     ~CleanupThread() { stop(); }
88 
89     // CleanupThread is neither copyable nor movable.
90     CleanupThread(const CleanupThread& other) = delete;
91     CleanupThread& operator=(const CleanupThread& other) = delete;
92     CleanupThread(CleanupThread&& other) = delete;
93     CleanupThread& operator=(CleanupThread&& other) = delete;
94 
enqueueCleanup(GenericCleanup command)95     void enqueueCleanup(GenericCleanup command) { mWorker.enqueue(std::move(command)); }
96 
waitForPendingCleanups()97     void waitForPendingCleanups() {
98         std::promise<void> pendingCleanupsCompletedSignal;
99         std::future<void> pendingCleanupsCompltedWaitable = pendingCleanupsCompletedSignal.get_future();
100         enqueueCleanup([&]() { pendingCleanupsCompletedSignal.set_value(); });
101         pendingCleanupsCompltedWaitable.wait();
102     }
103 
stop()104     void stop() {
105         mWorker.enqueue(Exit{});
106         mWorker.join();
107     }
108 
109    private:
110     struct Exit {};
111     using CleanupTask = std::variant<GenericCleanup, Exit>;
112     android::base::WorkerThread<CleanupTask> mWorker;
113 };
114 
115 VirtioGpuFrontend::VirtioGpuFrontend() = default;
116 
init(void * cookie,gfxstream::host::FeatureSet features,stream_renderer_fence_callback fence_callback)117 int VirtioGpuFrontend::init(void* cookie, gfxstream::host::FeatureSet features,
118                             stream_renderer_fence_callback fence_callback) {
119     stream_renderer_debug("cookie: %p", cookie);
120     mCookie = cookie;
121     mFeatures = features;
122     mFenceCallback = fence_callback;
123     mAddressSpaceDeviceControlOps = get_address_space_device_control_ops();
124     if (!mAddressSpaceDeviceControlOps) {
125         stream_renderer_error("Could not get address space device control ops!");
126         return -EINVAL;
127     }
128     mVirtioGpuTimelines = VirtioGpuTimelines::create(getFenceCompletionCallback());
129 
130 #if !defined(_WIN32)
131     mPageSize = getpagesize();
132 #endif
133 
134     mCleanupThread.reset(new CleanupThread());
135 
136     return 0;
137 }
138 
teardown()139 void VirtioGpuFrontend::teardown() {
140     destroyVirtioGpuObjects();
141 
142     mCleanupThread.reset();
143 }
144 
resetPipe(VirtioGpuContextId contextId,GoldfishHostPipe * hostPipe)145 int VirtioGpuFrontend::resetPipe(VirtioGpuContextId contextId, GoldfishHostPipe* hostPipe) {
146     stream_renderer_debug("reset pipe for context %u to hostpipe %p", contextId, hostPipe);
147 
148     auto contextIt = mContexts.find(contextId);
149     if (contextIt == mContexts.end()) {
150         stream_renderer_error("failed to reset pipe: context %u not found.", contextId);
151         return -EINVAL;
152     }
153     auto& context = contextIt->second;
154     context.SetHostPipe(hostPipe);
155 
156     // Also update any resources associated with it
157     for (auto resourceId : context.GetAttachedResources()) {
158         auto resourceIt = mResources.find(resourceId);
159         if (resourceIt == mResources.end()) {
160             stream_renderer_error("failed to reset pipe: resource %d not found.", resourceId);
161             return -EINVAL;
162         }
163         auto& resource = resourceIt->second;
164         resource.SetHostPipe(hostPipe);
165     }
166 
167     return 0;
168 }
169 
createContext(VirtioGpuCtxId contextId,uint32_t nlen,const char * name,uint32_t contextInit)170 int VirtioGpuFrontend::createContext(VirtioGpuCtxId contextId, uint32_t nlen, const char* name,
171                                      uint32_t contextInit) {
172     std::string contextName(name, nlen);
173 
174     stream_renderer_debug("ctxid: %u len: %u name: %s", contextId, nlen, contextName.c_str());
175     auto ops = ensureAndGetServiceOps();
176 
177     auto contextOpt = VirtioGpuContext::Create(ops, contextId, contextName, contextInit);
178     if (!contextOpt) {
179         stream_renderer_error("Failed to create context %u.", contextId);
180         return -EINVAL;
181     }
182     mContexts[contextId] = std::move(*contextOpt);
183     return 0;
184 }
185 
getFenceCompletionCallback()186 VirtioGpuTimelines::FenceCompletionCallback VirtioGpuFrontend::getFenceCompletionCallback() {
187     // Forwards fence completions from VirtioGpuTimelines to the client (VMM).
188     return [this](const VirtioGpuTimelines::Ring& ring, VirtioGpuTimelines::FenceId fenceId) {
189         struct stream_renderer_fence fence = {0};
190         fence.fence_id = fenceId;
191         fence.flags = STREAM_RENDERER_FLAG_FENCE;
192         if (const auto* contextSpecificRing = std::get_if<VirtioGpuRingContextSpecific>(&ring)) {
193             fence.flags |= STREAM_RENDERER_FLAG_FENCE_RING_IDX;
194             fence.ctx_id = contextSpecificRing->mCtxId;
195             fence.ring_idx = contextSpecificRing->mRingIdx;
196         }
197         mFenceCallback(mCookie, &fence);
198     };
199 }
200 
destroyContext(VirtioGpuCtxId contextId)201 int VirtioGpuFrontend::destroyContext(VirtioGpuCtxId contextId) {
202     stream_renderer_debug("ctxid: %u", contextId);
203 
204     auto contextIt = mContexts.find(contextId);
205     if (contextIt == mContexts.end()) {
206         stream_renderer_error("failed to destroy context %d: context not found", contextId);
207         return -EINVAL;
208     }
209     auto& context = contextIt->second;
210 
211     context.Destroy(ensureAndGetServiceOps(), mAddressSpaceDeviceControlOps);
212 
213     mContexts.erase(contextIt);
214     return 0;
215 }
216 
217 #define DECODE(variable, type, input) \
218     type variable = {};               \
219     memcpy(&variable, input, sizeof(type));
220 
addressSpaceProcessCmd(VirtioGpuCtxId ctxId,uint32_t * dwords)221 int VirtioGpuFrontend::addressSpaceProcessCmd(VirtioGpuCtxId ctxId, uint32_t* dwords) {
222     DECODE(header, gfxstream::gfxstreamHeader, dwords)
223 
224     auto contextIt = mContexts.find(ctxId);
225     if (contextIt == mContexts.end()) {
226         stream_renderer_error("ctx id %u not found", ctxId);
227         return -EINVAL;
228     }
229     auto& context = contextIt->second;
230 
231     switch (header.opCode) {
232         case GFXSTREAM_CONTEXT_CREATE: {
233             DECODE(contextCreate, gfxstream::gfxstreamContextCreate, dwords)
234 
235             auto resourceIt = mResources.find(contextCreate.resourceId);
236             if (resourceIt == mResources.end()) {
237                 stream_renderer_error("ASG coherent resource %u not found",
238                                       contextCreate.resourceId);
239                 return -EINVAL;
240             }
241             auto& resource = resourceIt->second;
242 
243             return context.CreateAddressSpaceGraphicsInstance(mAddressSpaceDeviceControlOps,
244                                                               resource);
245         }
246         case GFXSTREAM_CONTEXT_PING: {
247             DECODE(contextPing, gfxstream::gfxstreamContextPing, dwords)
248 
249             return context.PingAddressSpaceGraphicsInstance(mAddressSpaceDeviceControlOps,
250                                                             contextPing.resourceId);
251         }
252         default:
253             break;
254     }
255 
256     return 0;
257 }
258 
submitCmd(struct stream_renderer_command * cmd)259 int VirtioGpuFrontend::submitCmd(struct stream_renderer_command* cmd) {
260     if (!cmd) return -EINVAL;
261 
262     void* buffer = reinterpret_cast<void*>(cmd->cmd);
263 
264     VirtioGpuRing ring = VirtioGpuRingGlobal{};
265     stream_renderer_debug("ctx: % u, ring: %s buffer: %p dwords: %d", cmd->ctx_id,
266                           to_string(ring).c_str(), buffer, cmd->cmd_size);
267 
268     if (!buffer) {
269         stream_renderer_error("error: buffer null");
270         return -EINVAL;
271     }
272 
273     if (cmd->cmd_size < 4) {
274         stream_renderer_error("error: not enough bytes (got %d)", cmd->cmd_size);
275         return -EINVAL;
276     }
277 
278     DECODE(header, gfxstream::gfxstreamHeader, buffer);
279     switch (header.opCode) {
280         case GFXSTREAM_CONTEXT_CREATE:
281         case GFXSTREAM_CONTEXT_PING:
282         case GFXSTREAM_CONTEXT_PING_WITH_RESPONSE: {
283             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
284                                   "GFXSTREAM_CONTEXT_[CREATE|PING]");
285 
286             if (addressSpaceProcessCmd(cmd->ctx_id, (uint32_t*)buffer)) {
287                 return -EINVAL;
288             }
289             break;
290         }
291         case GFXSTREAM_CREATE_EXPORT_SYNC: {
292             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
293                                   "GFXSTREAM_CREATE_EXPORT_SYNC");
294 
295             // Make sure the context-specific ring is used
296             ring = VirtioGpuRingContextSpecific{
297                 .mCtxId = cmd->ctx_id,
298                 .mRingIdx = 0,
299             };
300 
301             DECODE(exportSync, gfxstream::gfxstreamCreateExportSync, buffer)
302 
303             uint64_t sync_handle = convert32to64(exportSync.syncHandleLo, exportSync.syncHandleHi);
304 
305             stream_renderer_debug("wait for gpu ring %s", to_string(ring).c_str());
306             auto taskId = mVirtioGpuTimelines->enqueueTask(ring);
307 #if GFXSTREAM_ENABLE_HOST_GLES
308             gfxstream::FrameBuffer::getFB()->asyncWaitForGpuWithCb(
309                 sync_handle, [this, taskId] { mVirtioGpuTimelines->notifyTaskCompletion(taskId); });
310 #endif
311             break;
312         }
313         case GFXSTREAM_CREATE_EXPORT_SYNC_VK:
314         case GFXSTREAM_CREATE_IMPORT_SYNC_VK: {
315             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
316                                   "GFXSTREAM_CREATE_[IMPORT|EXPORT]_SYNC_VK");
317 
318             // The guest sync export assumes fence context support and always uses
319             // VIRTGPU_EXECBUF_RING_IDX. With this, the task created here must use
320             // the same ring as the fence created for the virtio gpu command or the
321             // fence may be signaled without properly waiting for the task to complete.
322             ring = VirtioGpuRingContextSpecific{
323                 .mCtxId = cmd->ctx_id,
324                 .mRingIdx = 0,
325             };
326 
327             DECODE(exportSyncVK, gfxstream::gfxstreamCreateExportSyncVK, buffer)
328 
329             uint64_t device_handle =
330                 convert32to64(exportSyncVK.deviceHandleLo, exportSyncVK.deviceHandleHi);
331 
332             uint64_t fence_handle =
333                 convert32to64(exportSyncVK.fenceHandleLo, exportSyncVK.fenceHandleHi);
334 
335             stream_renderer_debug("wait for gpu ring %s", to_string(ring).c_str());
336             auto taskId = mVirtioGpuTimelines->enqueueTask(ring);
337             gfxstream::FrameBuffer::getFB()->asyncWaitForGpuVulkanWithCb(
338                 device_handle, fence_handle,
339                 [this, taskId] { mVirtioGpuTimelines->notifyTaskCompletion(taskId); });
340             break;
341         }
342         case GFXSTREAM_CREATE_QSRI_EXPORT_VK: {
343             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
344                                   "GFXSTREAM_CREATE_QSRI_EXPORT_VK");
345 
346             // The guest QSRI export assumes fence context support and always uses
347             // VIRTGPU_EXECBUF_RING_IDX. With this, the task created here must use
348             // the same ring as the fence created for the virtio gpu command or the
349             // fence may be signaled without properly waiting for the task to complete.
350             ring = VirtioGpuRingContextSpecific{
351                 .mCtxId = cmd->ctx_id,
352                 .mRingIdx = 0,
353             };
354 
355             DECODE(exportQSRI, gfxstream::gfxstreamCreateQSRIExportVK, buffer)
356 
357             uint64_t image_handle =
358                 convert32to64(exportQSRI.imageHandleLo, exportQSRI.imageHandleHi);
359 
360             stream_renderer_debug("wait for gpu vk qsri ring %u image 0x%llx",
361                                   to_string(ring).c_str(), (unsigned long long)image_handle);
362             auto taskId = mVirtioGpuTimelines->enqueueTask(ring);
363             gfxstream::FrameBuffer::getFB()->asyncWaitForGpuVulkanQsriWithCb(
364                 image_handle,
365                 [this, taskId] { mVirtioGpuTimelines->notifyTaskCompletion(taskId); });
366             break;
367         }
368         case GFXSTREAM_RESOURCE_CREATE_3D: {
369             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
370                                   "GFXSTREAM_RESOURCE_CREATE_3D");
371 
372             DECODE(create3d, gfxstream::gfxstreamResourceCreate3d, buffer)
373             struct stream_renderer_resource_create_args rc3d = {0};
374 
375             rc3d.target = create3d.target;
376             rc3d.format = create3d.format;
377             rc3d.bind = create3d.bind;
378             rc3d.width = create3d.width;
379             rc3d.height = create3d.height;
380             rc3d.depth = create3d.depth;
381             rc3d.array_size = create3d.arraySize;
382             rc3d.last_level = create3d.lastLevel;
383             rc3d.nr_samples = create3d.nrSamples;
384             rc3d.flags = create3d.flags;
385 
386             auto contextIt = mContexts.find(cmd->ctx_id);
387             if (contextIt == mContexts.end()) {
388                 stream_renderer_error("ctx id %u is not found", cmd->ctx_id);
389                 return -EINVAL;
390             }
391             auto& context = contextIt->second;
392 
393             return context.AddPendingBlob(create3d.blobId, rc3d);
394         }
395         case GFXSTREAM_ACQUIRE_SYNC: {
396             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
397                                   "GFXSTREAM_ACQUIRE_SYNC");
398 
399             DECODE(acquireSync, gfxstream::gfxstreamAcquireSync, buffer);
400 
401             auto contextIt = mContexts.find(cmd->ctx_id);
402             if (contextIt == mContexts.end()) {
403                 stream_renderer_error("ctx id %u is not found", cmd->ctx_id);
404                 return -EINVAL;
405             }
406             auto& context = contextIt->second;
407             return context.AcquireSync(acquireSync.syncId);
408         }
409         case GFXSTREAM_PLACEHOLDER_COMMAND_VK: {
410             GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_STREAM_RENDERER_CATEGORY,
411                                   "GFXSTREAM_PLACEHOLDER_COMMAND_VK");
412 
413             // Do nothing, this is a placeholder command
414             break;
415         }
416         default:
417             return -EINVAL;
418     }
419 
420     return 0;
421 }
422 
createFence(uint64_t fence_id,const VirtioGpuRing & ring)423 int VirtioGpuFrontend::createFence(uint64_t fence_id, const VirtioGpuRing& ring) {
424     stream_renderer_debug("fenceid: %llu ring: %s", (unsigned long long)fence_id,
425                           to_string(ring).c_str());
426 
427     mVirtioGpuTimelines->enqueueFence(ring, fence_id);
428 
429     return 0;
430 }
431 
acquireContextFence(uint32_t contextId,uint64_t fenceId)432 int VirtioGpuFrontend::acquireContextFence(uint32_t contextId, uint64_t fenceId) {
433     auto contextIt = mContexts.find(contextId);
434     if (contextIt == mContexts.end()) {
435         stream_renderer_error("failed to acquire context %u fence: context not found", contextId);
436         return -EINVAL;
437     }
438     auto& context = contextIt->second;
439 
440     auto syncInfoOpt = context.TakeSync();
441     if (!syncInfoOpt) {
442         stream_renderer_error("failed to acquire context %u fence: no sync acquired", contextId);
443         return -EINVAL;
444     }
445 
446     mSyncMap[fenceId] = std::make_shared<gfxstream::SyncDescriptorInfo>(std::move(*syncInfoOpt));
447 
448     return 0;
449 }
450 
poll()451 void VirtioGpuFrontend::poll() { mVirtioGpuTimelines->poll(); }
452 
createResource(struct stream_renderer_resource_create_args * args,struct iovec * iov,uint32_t num_iovs)453 int VirtioGpuFrontend::createResource(struct stream_renderer_resource_create_args* args,
454                                       struct iovec* iov, uint32_t num_iovs) {
455     auto resourceOpt = VirtioGpuResource::Create(args, iov, num_iovs);
456     if (!resourceOpt) {
457         stream_renderer_error("Failed to create resource %u.", args->handle);
458         return -EINVAL;
459     }
460     mResources[args->handle] = std::move(*resourceOpt);
461     return 0;
462 }
463 
importResource(uint32_t res_handle,const struct stream_renderer_handle * import_handle,const struct stream_renderer_import_data * import_data)464 int VirtioGpuFrontend::importResource(uint32_t res_handle,
465                                       const struct stream_renderer_handle* import_handle,
466                                       const struct stream_renderer_import_data* import_data) {
467     if (!import_handle) {
468         stream_renderer_error(
469             "import_handle was not provided in call to importResource for handle: %d", res_handle);
470         return -EINVAL;
471     } else if (import_data && (import_data->flags & STREAM_RENDERER_IMPORT_FLAG_RESOURCE_EXISTS)) {
472         auto resourceIt = mResources.find(res_handle);
473         if (resourceIt == mResources.end()) {
474             stream_renderer_error(
475                 "import_data::flags specified STREAM_RENDERER_IMPORT_FLAG_RESOURCE_EXISTS, but "
476                 "internal resource does not already exist",
477                 res_handle);
478             return -EINVAL;
479         }
480         return resourceIt->second.ImportHandle(import_handle, import_data);
481     } else {
482         auto resourceOpt = VirtioGpuResource::Create(res_handle, import_handle, import_data);
483         if (!resourceOpt) {
484             stream_renderer_error("Failed to create resource %u, with import_handle/import_data",
485                                   res_handle);
486             return -EINVAL;
487         }
488         mResources[res_handle] = std::move(*resourceOpt);
489         return 0;
490     }
491 }
492 
unrefResource(uint32_t resourceId)493 void VirtioGpuFrontend::unrefResource(uint32_t resourceId) {
494     stream_renderer_debug("resource: %u", resourceId);
495 
496     auto resourceIt = mResources.find(resourceId);
497     if (resourceIt == mResources.end()) return;
498     auto& resource = resourceIt->second;
499 
500     auto attachedContextIds = resource.GetAttachedContexts();
501     for (auto contextId : attachedContextIds) {
502         detachResource(contextId, resourceId);
503     }
504 
505     resource.Destroy();
506 
507     mResources.erase(resourceIt);
508 }
509 
attachIov(int resourceId,struct iovec * iov,int num_iovs)510 int VirtioGpuFrontend::attachIov(int resourceId, struct iovec* iov, int num_iovs) {
511     stream_renderer_debug("resource:%d numiovs: %d", resourceId, num_iovs);
512 
513     auto it = mResources.find(resourceId);
514     if (it == mResources.end()) {
515         stream_renderer_error("failed to attach iov: resource %u not found.", resourceId);
516         return ENOENT;
517     }
518     auto& resource = it->second;
519     resource.AttachIov(iov, num_iovs);
520     return 0;
521 }
522 
detachIov(int resourceId)523 void VirtioGpuFrontend::detachIov(int resourceId) {
524     stream_renderer_debug("resource:%d", resourceId);
525 
526     auto it = mResources.find(resourceId);
527     if (it == mResources.end()) {
528         stream_renderer_error("failed to detach iov: resource %u not found.", resourceId);
529         return;
530     }
531     auto& resource = it->second;
532     resource.DetachIov();
533 }
534 
535 namespace {
536 
AsVecOption(struct iovec * iov,int iovec_cnt)537 std::optional<std::vector<struct iovec>> AsVecOption(struct iovec* iov, int iovec_cnt) {
538     if (iovec_cnt > 0) {
539         std::vector<struct iovec> ret;
540         ret.reserve(iovec_cnt);
541         for (int i = 0; i < iovec_cnt; i++) {
542             ret.push_back(iov[i]);
543         }
544         return ret;
545     }
546     return std::nullopt;
547 }
548 
549 }  // namespace
550 
transferReadIov(int resId,uint64_t offset,stream_renderer_box * box,struct iovec * iov,int iovec_cnt)551 int VirtioGpuFrontend::transferReadIov(int resId, uint64_t offset, stream_renderer_box* box,
552                                        struct iovec* iov, int iovec_cnt) {
553     auto it = mResources.find(resId);
554     if (it == mResources.end()) {
555         stream_renderer_error("Failed to transfer: failed to find resource %d.", resId);
556         return EINVAL;
557     }
558     auto& resource = it->second;
559 
560     auto ops = ensureAndGetServiceOps();
561     return resource.TransferRead(ops, offset, box, AsVecOption(iov, iovec_cnt));
562 }
563 
transferWriteIov(int resId,uint64_t offset,stream_renderer_box * box,struct iovec * iov,int iovec_cnt)564 int VirtioGpuFrontend::transferWriteIov(int resId, uint64_t offset, stream_renderer_box* box,
565                                         struct iovec* iov, int iovec_cnt) {
566     auto it = mResources.find(resId);
567     if (it == mResources.end()) {
568         stream_renderer_error("Failed to transfer: failed to find resource %d.", resId);
569         return EINVAL;
570     }
571     auto& resource = it->second;
572 
573     auto ops = ensureAndGetServiceOps();
574     auto result = resource.TransferWrite(ops, offset, box, AsVecOption(iov, iovec_cnt));
575     if (result.status != 0) return result.status;
576 
577     if (result.contextPipe) {
578         resetPipe(result.contextId, result.contextPipe);
579     }
580     return 0;
581 }
582 
getCapset(uint32_t set,uint32_t * max_size)583 void VirtioGpuFrontend::getCapset(uint32_t set, uint32_t* max_size) {
584     switch (set) {
585         case VIRTGPU_CAPSET_GFXSTREAM_VULKAN:
586             *max_size = sizeof(struct gfxstream::vulkanCapset);
587             break;
588         case VIRTGPU_CAPSET_GFXSTREAM_MAGMA:
589             *max_size = sizeof(struct gfxstream::magmaCapset);
590             break;
591         case VIRTGPU_CAPSET_GFXSTREAM_GLES:
592             *max_size = sizeof(struct gfxstream::glesCapset);
593             break;
594         case VIRTGPU_CAPSET_GFXSTREAM_COMPOSER:
595             *max_size = sizeof(struct gfxstream::composerCapset);
596             break;
597         default:
598             stream_renderer_error("Incorrect capability set specified (%u)", set);
599     }
600 }
601 
fillCaps(uint32_t set,void * caps)602 void VirtioGpuFrontend::fillCaps(uint32_t set, void* caps) {
603     switch (set) {
604         case VIRTGPU_CAPSET_GFXSTREAM_VULKAN: {
605             struct gfxstream::vulkanCapset* capset =
606                 reinterpret_cast<struct gfxstream::vulkanCapset*>(caps);
607 
608             memset(capset, 0, sizeof(*capset));
609 
610             capset->protocolVersion = 1;
611             capset->ringSize = 12288;
612             capset->bufferSize = 1048576;
613 
614             auto* fb = gfxstream::FrameBuffer::getFB();
615             if (fb->hasEmulationVk()) {
616                 const auto info = fb->getEmulationVk().getRepresentativeColorBufferMemoryTypeInfo();
617                 capset->colorBufferMemoryIndex = info.guestMemoryTypeIndex;
618                 capset->deferredMapping = 1;
619             }
620 
621             if (mFeatures.VulkanBatchedDescriptorSetUpdate.enabled) {
622                 capset->vulkanBatchedDescriptorSetUpdate=1;
623             }
624             capset->noRenderControlEnc = 1;
625             capset->blobAlignment = mPageSize;
626 
627 #if GFXSTREAM_UNSTABLE_VULKAN_DMABUF_WINSYS
628             capset->alwaysBlob = 1;
629 #endif
630 
631 #if GFXSTREAM_UNSTABLE_VULKAN_EXTERNAL_SYNC
632             capset->externalSync = 1;
633 #endif
634 
635             memset(capset->virglSupportedFormats, 0, sizeof(capset->virglSupportedFormats));
636 
637             struct FormatWithName {
638                 uint32_t format;
639                 const char* name;
640             };
641 #define MAKE_FORMAT_AND_NAME(x) \
642     {                           \
643         x, #x                   \
644     }
645             static const FormatWithName kPossibleFormats[] = {
646                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_B5G6R5_UNORM),
647                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_B8G8R8A8_UNORM),
648                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_B8G8R8X8_UNORM),
649                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_NV12),
650                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_P010),
651                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R10G10B10A2_UNORM),
652                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R16_UNORM),
653                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R16G16B16A16_FLOAT),
654                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R8_UNORM),
655                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R8G8_UNORM),
656                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R8G8B8_UNORM),
657                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R8G8B8A8_UNORM),
658                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_R8G8B8X8_UNORM),
659                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_YV12),
660                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_Z16_UNORM),
661                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_Z24_UNORM_S8_UINT),
662                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_Z24X8_UNORM),
663                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_Z32_FLOAT_S8X24_UINT),
664                 MAKE_FORMAT_AND_NAME(VIRGL_FORMAT_Z32_FLOAT),
665             };
666 #undef MAKE_FORMAT_AND_NAME
667 
668             stream_renderer_info("Format support:");
669             for (std::size_t i = 0; i < std::size(kPossibleFormats); i++) {
670                 const FormatWithName& possibleFormat = kPossibleFormats[i];
671 
672                 GLenum possibleFormatGl = virgl_format_to_gl(possibleFormat.format);
673                 const bool supported =
674                     gfxstream::FrameBuffer::getFB()->isFormatSupported(possibleFormatGl);
675 
676                 stream_renderer_info(" %s: %s", possibleFormat.name,
677                                      (supported ? "supported" : "unsupported"));
678                 set_virgl_format_supported(capset->virglSupportedFormats, possibleFormat.format,
679                                            supported);
680             }
681             break;
682         }
683         case VIRTGPU_CAPSET_GFXSTREAM_MAGMA: {
684             struct gfxstream::magmaCapset* capset =
685                 reinterpret_cast<struct gfxstream::magmaCapset*>(caps);
686 
687             capset->protocolVersion = 1;
688             capset->ringSize = 12288;
689             capset->bufferSize = 1048576;
690             capset->blobAlignment = mPageSize;
691             break;
692         }
693         case VIRTGPU_CAPSET_GFXSTREAM_GLES: {
694             struct gfxstream::glesCapset* capset =
695                 reinterpret_cast<struct gfxstream::glesCapset*>(caps);
696 
697             capset->protocolVersion = 1;
698             capset->ringSize = 12288;
699             capset->bufferSize = 1048576;
700             capset->blobAlignment = mPageSize;
701             break;
702         }
703         case VIRTGPU_CAPSET_GFXSTREAM_COMPOSER: {
704             struct gfxstream::composerCapset* capset =
705                 reinterpret_cast<struct gfxstream::composerCapset*>(caps);
706 
707             capset->protocolVersion = 1;
708             capset->ringSize = 12288;
709             capset->bufferSize = 1048576;
710             capset->blobAlignment = mPageSize;
711             break;
712         }
713         default:
714             stream_renderer_error("Incorrect capability set specified");
715     }
716 }
717 
attachResource(uint32_t contextId,uint32_t resourceId)718 void VirtioGpuFrontend::attachResource(uint32_t contextId, uint32_t resourceId) {
719     stream_renderer_debug("ctxid: %u resid: %u", contextId, resourceId);
720 
721     auto contextIt = mContexts.find(contextId);
722     if (contextIt == mContexts.end()) {
723         stream_renderer_error("failed to attach resource %u to context %u: context not found.",
724                               resourceId, contextId);
725         return;
726     }
727     auto& context = contextIt->second;
728 
729     auto resourceIt = mResources.find(resourceId);
730     if (resourceIt == mResources.end()) {
731         stream_renderer_error("failed to attach resource %u to context %u: resource not found.",
732                               resourceId, contextId);
733         return;
734     }
735     auto& resource = resourceIt->second;
736 
737     context.AttachResource(resource);
738 }
739 
detachResource(uint32_t contextId,uint32_t resourceId)740 void VirtioGpuFrontend::detachResource(uint32_t contextId, uint32_t resourceId) {
741     stream_renderer_debug("ctxid: %u resid: %u", contextId, resourceId);
742 
743     auto contextIt = mContexts.find(contextId);
744     if (contextIt == mContexts.end()) {
745         stream_renderer_error("failed to detach resource %u to context %u: context not found.",
746                               resourceId, contextId);
747         return;
748     }
749     auto& context = contextIt->second;
750 
751     auto resourceIt = mResources.find(resourceId);
752     if (resourceIt == mResources.end()) {
753         stream_renderer_error("failed to attach resource %u to context %u: resource not found.",
754                               resourceId, contextId);
755         return;
756     }
757     auto& resource = resourceIt->second;
758 
759     auto resourceAsgOpt = context.TakeAddressSpaceGraphicsHandle(resourceId);
760     if (resourceAsgOpt) {
761         mCleanupThread->enqueueCleanup(
762             [this, asgBlob = resource.ShareRingBlob(), asgHandle = *resourceAsgOpt]() {
763                 mAddressSpaceDeviceControlOps->destroy_handle(asgHandle);
764             });
765     }
766 
767     context.DetachResource(resource);
768 }
769 
getResourceInfo(uint32_t resourceId,struct stream_renderer_resource_info * info)770 int VirtioGpuFrontend::getResourceInfo(uint32_t resourceId,
771                                        struct stream_renderer_resource_info* info) {
772     stream_renderer_debug("resource: %u", resourceId);
773 
774     if (!info) {
775         stream_renderer_error("Failed to get info: invalid info struct.");
776         return EINVAL;
777     }
778 
779     auto resourceIt = mResources.find(resourceId);
780     if (resourceIt == mResources.end()) {
781         stream_renderer_error("Failed to get info: failed to find resource %d.", resourceId);
782         return ENOENT;
783     }
784     auto& resource = resourceIt->second;
785     return resource.GetInfo(info);
786 }
787 
flushResource(uint32_t res_handle)788 void VirtioGpuFrontend::flushResource(uint32_t res_handle) {
789     auto taskId = mVirtioGpuTimelines->enqueueTask(VirtioGpuRingGlobal{});
790     gfxstream::FrameBuffer::getFB()->postWithCallback(
791         res_handle, [this, taskId](std::shared_future<void> waitForGpu) {
792             waitForGpu.wait();
793             mVirtioGpuTimelines->notifyTaskCompletion(taskId);
794         });
795 }
796 
createBlob(uint32_t contextId,uint32_t resourceId,const struct stream_renderer_create_blob * createBlobArgs,const struct stream_renderer_handle * handle)797 int VirtioGpuFrontend::createBlob(uint32_t contextId, uint32_t resourceId,
798                                   const struct stream_renderer_create_blob* createBlobArgs,
799                                   const struct stream_renderer_handle* handle) {
800     auto contextIt = mContexts.find(contextId);
801     if (contextIt == mContexts.end()) {
802         stream_renderer_error("failed to create blob resource %u: context %u missing.", resourceId,
803                               contextId);
804         return -EINVAL;
805     }
806     auto& context = contextIt->second;
807 
808     auto createArgs = context.TakePendingBlob(createBlobArgs->blob_id);
809     if (createArgs) {
810         createArgs->handle = resourceId;
811     }
812 
813     auto resourceOpt =
814         VirtioGpuResource::Create(mFeatures, mPageSize, contextId, resourceId,
815                                   createArgs ? &*createArgs : nullptr, createBlobArgs, handle);
816     if (!resourceOpt) {
817         stream_renderer_error("failed to create blob resource %u.", resourceId);
818         return -EINVAL;
819     }
820     mResources[resourceId] = std::move(*resourceOpt);
821     return 0;
822 }
823 
resourceMap(uint32_t resourceId,void ** hvaOut,uint64_t * sizeOut)824 int VirtioGpuFrontend::resourceMap(uint32_t resourceId, void** hvaOut, uint64_t* sizeOut) {
825     stream_renderer_debug("resource: %u", resourceId);
826 
827     if (mFeatures.ExternalBlob.enabled) {
828         stream_renderer_error("Failed to map resource: external blob enabled.");
829         return -EINVAL;
830     }
831 
832     auto it = mResources.find(resourceId);
833     if (it == mResources.end()) {
834         if (hvaOut) *hvaOut = nullptr;
835         if (sizeOut) *sizeOut = 0;
836 
837         stream_renderer_error("Failed to map resource: unknown resource id %d.", resourceId);
838         return -EINVAL;
839     }
840 
841     auto& resource = it->second;
842     return resource.Map(hvaOut, sizeOut);
843 }
844 
resourceUnmap(uint32_t resourceId)845 int VirtioGpuFrontend::resourceUnmap(uint32_t resourceId) {
846     stream_renderer_debug("resource: %u", resourceId);
847 
848     auto it = mResources.find(resourceId);
849     if (it == mResources.end()) {
850         stream_renderer_error("Failed to map resource: unknown resource id %d.", resourceId);
851         return -EINVAL;
852     }
853 
854     // TODO(lfy): Good place to run any registered cleanup callbacks.
855     // No-op for now.
856     return 0;
857 }
858 
platformCreateSharedEglContext()859 void* VirtioGpuFrontend::platformCreateSharedEglContext() {
860     void* ptr = nullptr;
861 #if GFXSTREAM_ENABLE_HOST_GLES
862     ptr = gfxstream::FrameBuffer::getFB()->platformCreateSharedEglContext();
863 #endif
864     return ptr;
865 }
866 
platformDestroySharedEglContext(void * context)867 int VirtioGpuFrontend::platformDestroySharedEglContext(void* context) {
868     bool success = false;
869 #if GFXSTREAM_ENABLE_HOST_GLES
870     success = gfxstream::FrameBuffer::getFB()->platformDestroySharedEglContext(context);
871 #endif
872     return success ? 0 : -1;
873 }
874 
resourceMapInfo(uint32_t resourceId,uint32_t * map_info)875 int VirtioGpuFrontend::resourceMapInfo(uint32_t resourceId, uint32_t* map_info) {
876     stream_renderer_debug("resource: %u", resourceId);
877 
878     auto resourceIt = mResources.find(resourceId);
879     if (resourceIt == mResources.end()) {
880         stream_renderer_error("Failed to get resource map info: unknown resource %d.", resourceId);
881         return -EINVAL;
882     }
883 
884     const auto& resource = resourceIt->second;
885     return resource.GetCaching(map_info);
886 }
887 
exportBlob(uint32_t resourceId,struct stream_renderer_handle * handle)888 int VirtioGpuFrontend::exportBlob(uint32_t resourceId, struct stream_renderer_handle* handle) {
889     stream_renderer_debug("resource: %u", resourceId);
890 
891     auto resourceIt = mResources.find(resourceId);
892     if (resourceIt == mResources.end()) {
893         stream_renderer_error("Failed to export blob: unknown resource %d.", resourceId);
894         return -EINVAL;
895     }
896     auto& resource = resourceIt->second;
897     return resource.ExportBlob(handle);
898 }
899 
exportFence(uint64_t fenceId,struct stream_renderer_handle * handle)900 int VirtioGpuFrontend::exportFence(uint64_t fenceId, struct stream_renderer_handle* handle) {
901     auto it = mSyncMap.find(fenceId);
902     if (it == mSyncMap.end()) {
903         return -EINVAL;
904     }
905 
906     auto& entry = it->second;
907     DescriptorType rawDescriptor;
908     auto rawDescriptorOpt = entry->descriptor.release();
909     if (rawDescriptorOpt)
910         rawDescriptor = *rawDescriptorOpt;
911     else
912         return -EINVAL;
913 
914     handle->handle_type = entry->streamHandleType;
915 
916 #ifdef _WIN32
917     handle->os_handle = static_cast<int64_t>(reinterpret_cast<intptr_t>(rawDescriptor));
918 #else
919     handle->os_handle = static_cast<int64_t>(rawDescriptor);
920 #endif
921 
922     return 0;
923 }
924 
vulkanInfo(uint32_t resourceId,struct stream_renderer_vulkan_info * vulkanInfo)925 int VirtioGpuFrontend::vulkanInfo(uint32_t resourceId,
926                                   struct stream_renderer_vulkan_info* vulkanInfo) {
927     auto resourceIt = mResources.find(resourceId);
928     if (resourceIt == mResources.end()) {
929         stream_renderer_error("failed to get vulkan info: failed to find resource %d", resourceId);
930         return -EINVAL;
931     }
932     auto& resource = resourceIt->second;
933     return resource.GetVulkanInfo(vulkanInfo);
934 }
935 
destroyVirtioGpuObjects()936 int VirtioGpuFrontend::destroyVirtioGpuObjects() {
937     {
938         std::vector<VirtioGpuResourceId> resourceIds;
939         resourceIds.reserve(mResources.size());
940         for (auto& [resourceId, resource] : mResources) {
941             const auto contextIds = resource.GetAttachedContexts();
942             for (const VirtioGpuContextId contextId : contextIds) {
943                 detachResource(contextId, resourceId);
944             }
945             resourceIds.push_back(resourceId);
946         }
947         for (const VirtioGpuResourceId resourceId : resourceIds) {
948             unrefResource(resourceId);
949         }
950         mResources.clear();
951     }
952     {
953         std::vector<VirtioGpuContextId> contextIds;
954         contextIds.reserve(mContexts.size());
955         for (const auto& [contextId, _] : mContexts) {
956             contextIds.push_back(contextId);
957         }
958         for (const VirtioGpuContextId contextId : contextIds) {
959             destroyContext(contextId);
960         }
961         mContexts.clear();
962     }
963 
964     if (mCleanupThread) {
965         mCleanupThread->waitForPendingCleanups();
966     }
967 
968     return 0;
969 }
970 
971 #ifdef CONFIG_AEMU
setServiceOps(const GoldfishPipeServiceOps * ops)972 void VirtioGpuFrontend::setServiceOps(const GoldfishPipeServiceOps* ops) { mServiceOps = ops; }
973 #endif  // CONFIG_AEMU
974 
ensureAndGetServiceOps()975 inline const GoldfishPipeServiceOps* VirtioGpuFrontend::ensureAndGetServiceOps() {
976     if (mServiceOps) return mServiceOps;
977     mServiceOps = goldfish_pipe_get_service_ops();
978     return mServiceOps;
979 }
980 
981 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
982 
983 static constexpr const char kSnapshotBasenameAsg[] = "gfxstream_asg.bin";
984 static constexpr const char kSnapshotBasenameFrontend[] = "gfxstream_frontend.txtproto";
985 static constexpr const char kSnapshotBasenameRenderer[] = "gfxstream_renderer.bin";
986 
snapshotRenderer(const char * directory)987 int VirtioGpuFrontend::snapshotRenderer(const char* directory) {
988     const std::filesystem::path snapshotDirectory = std::string(directory);
989     const std::filesystem::path snapshotPath = snapshotDirectory / kSnapshotBasenameRenderer;
990 
991     android::base::StdioStream stream(fopen(snapshotPath.c_str(), "wb"),
992                                       android::base::StdioStream::kOwner);
993     android::snapshot::SnapshotSaveStream saveStream{
994         .stream = &stream,
995     };
996 
997     android_getOpenglesRenderer()->save(saveStream.stream, saveStream.textureSaver);
998     return 0;
999 }
1000 
snapshotFrontend(const char * directory)1001 int VirtioGpuFrontend::snapshotFrontend(const char* directory) {
1002     gfxstream::host::snapshot::VirtioGpuFrontendSnapshot snapshot;
1003 
1004     for (const auto& [contextId, context] : mContexts) {
1005         auto contextSnapshotOpt = context.Snapshot();
1006         if (!contextSnapshotOpt) {
1007             stream_renderer_error("Failed to snapshot context %d", contextId);
1008             return -1;
1009         }
1010         (*snapshot.mutable_contexts())[contextId] = std::move(*contextSnapshotOpt);
1011     }
1012     for (const auto& [resourceId, resource] : mResources) {
1013         auto resourceSnapshotOpt = resource.Snapshot();
1014         if (!resourceSnapshotOpt) {
1015             stream_renderer_error("Failed to snapshot resource %d", resourceId);
1016             return -1;
1017         }
1018         (*snapshot.mutable_resources())[resourceId] = std::move(*resourceSnapshotOpt);
1019     }
1020 
1021     if (mVirtioGpuTimelines) {
1022         auto timelinesSnapshotOpt = mVirtioGpuTimelines->Snapshot();
1023         if (!timelinesSnapshotOpt) {
1024             stream_renderer_error("Failed to snapshot timelines.");
1025             return -1;
1026         }
1027         snapshot.mutable_timelines()->Swap(&*timelinesSnapshotOpt);
1028     }
1029 
1030     const std::filesystem::path snapshotDirectory = std::string(directory);
1031     const std::filesystem::path snapshotPath = snapshotDirectory / kSnapshotBasenameFrontend;
1032     int snapshotFd = open(snapshotPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0660);
1033     if (snapshotFd < 0) {
1034         stream_renderer_error("Failed to save snapshot: failed to open %s", snapshotPath.c_str());
1035         return -1;
1036     }
1037     google::protobuf::io::FileOutputStream snapshotOutputStream(snapshotFd);
1038     snapshotOutputStream.SetCloseOnDelete(true);
1039     if (!google::protobuf::TextFormat::Print(snapshot, &snapshotOutputStream)) {
1040         stream_renderer_error("Failed to save snapshot: failed to serialize to stream.");
1041         return -1;
1042     }
1043 
1044     return 0;
1045 }
1046 
snapshotAsg(const char * directory)1047 int VirtioGpuFrontend::snapshotAsg(const char* directory) {
1048     const std::filesystem::path snapshotDirectory = std::string(directory);
1049     const std::filesystem::path snapshotPath = snapshotDirectory / kSnapshotBasenameAsg;
1050 
1051     android::base::StdioStream stream(fopen(snapshotPath.c_str(), "wb"),
1052                                       android::base::StdioStream::kOwner);
1053     android::snapshot::SnapshotLoadStream saveStream{
1054         .stream = &stream,
1055     };
1056 
1057     int ret = android::emulation::goldfish_address_space_memory_state_save(saveStream.stream);
1058     if (ret) {
1059         stream_renderer_error("Failed to save snapshot: failed to save ASG state.");
1060         return ret;
1061     }
1062     return 0;
1063 }
1064 
snapshot(const char * directory)1065 int VirtioGpuFrontend::snapshot(const char* directory) {
1066     stream_renderer_debug("directory:%s", directory);
1067 
1068     android_getOpenglesRenderer()->pauseAllPreSave();
1069 
1070     int ret = snapshotRenderer(directory);
1071     if (ret) {
1072         stream_renderer_error("Failed to save snapshot: failed to snapshot renderer.");
1073         return ret;
1074     }
1075 
1076     ret = snapshotFrontend(directory);
1077     if (ret) {
1078         stream_renderer_error("Failed to save snapshot: failed to snapshot frontend.");
1079         return ret;
1080     }
1081 
1082     ret = snapshotAsg(directory);
1083     if (ret) {
1084         stream_renderer_error("Failed to save snapshot: failed to snapshot ASG device.");
1085         return ret;
1086     }
1087 
1088     stream_renderer_debug("directory:%s - done!", directory);
1089     return 0;
1090 }
1091 
restoreRenderer(const char * directory)1092 int VirtioGpuFrontend::restoreRenderer(const char* directory) {
1093     const std::filesystem::path snapshotDirectory = std::string(directory);
1094     const std::filesystem::path snapshotPath = snapshotDirectory / kSnapshotBasenameRenderer;
1095 
1096     android::base::StdioStream stream(fopen(snapshotPath.c_str(), "rb"),
1097                                       android::base::StdioStream::kOwner);
1098     android::snapshot::SnapshotLoadStream loadStream{
1099         .stream = &stream,
1100     };
1101 
1102     android_getOpenglesRenderer()->load(loadStream.stream, loadStream.textureLoader);
1103     return 0;
1104 }
1105 
restoreFrontend(const char * directory)1106 int VirtioGpuFrontend::restoreFrontend(const char* directory) {
1107     const std::filesystem::path snapshotDirectory = std::string(directory);
1108     const std::filesystem::path snapshotPath = snapshotDirectory / kSnapshotBasenameFrontend;
1109 
1110     gfxstream::host::snapshot::VirtioGpuFrontendSnapshot snapshot;
1111     {
1112         int snapshotFd = open(snapshotPath.c_str(), O_RDONLY);
1113         if (snapshotFd < 0) {
1114             stream_renderer_error("Failed to restore snapshot: failed to open %s",
1115                                 snapshotPath.c_str());
1116             return -1;
1117         }
1118         google::protobuf::io::FileInputStream snapshotInputStream(snapshotFd);
1119         snapshotInputStream.SetCloseOnDelete(true);
1120         if (!google::protobuf::TextFormat::Parse(&snapshotInputStream, &snapshot)) {
1121             stream_renderer_error("Failed to restore snapshot: failed to parse from file.");
1122             return -1;
1123         }
1124     }
1125 
1126     mContexts.clear();
1127     mResources.clear();
1128 
1129     for (const auto& [contextId, contextSnapshot] : snapshot.contexts()) {
1130         auto contextOpt = VirtioGpuContext::Restore(contextSnapshot);
1131         if (!contextOpt) {
1132             stream_renderer_error("Failed to restore context %d", contextId);
1133             return -1;
1134         }
1135         mContexts.emplace(contextId, std::move(*contextOpt));
1136     }
1137     for (const auto& [resourceId, resourceSnapshot] : snapshot.resources()) {
1138         auto resourceOpt = VirtioGpuResource::Restore(resourceSnapshot);
1139         if (!resourceOpt) {
1140             stream_renderer_error("Failed to restore resource %d", resourceId);
1141             return -1;
1142         }
1143         mResources.emplace(resourceId, std::move(*resourceOpt));
1144     }
1145 
1146     mVirtioGpuTimelines =
1147         VirtioGpuTimelines::Restore(getFenceCompletionCallback(), snapshot.timelines());
1148     if (!mVirtioGpuTimelines) {
1149         stream_renderer_error("Failed to restore timelines.");
1150         return -1;
1151     }
1152 
1153     return 0;
1154 }
1155 
restoreAsg(const char * directory)1156 int VirtioGpuFrontend::restoreAsg(const char* directory) {
1157     const std::filesystem::path snapshotDirectory = std::string(directory);
1158     const std::filesystem::path snapshotPath = snapshotDirectory / kSnapshotBasenameAsg;
1159 
1160     android::base::StdioStream stream(fopen(snapshotPath.c_str(), "rb"),
1161                                       android::base::StdioStream::kOwner);
1162     android::snapshot::SnapshotLoadStream loadStream{
1163         .stream = &stream,
1164     };
1165 
1166     // Gather external memory info that the ASG device needs to reload.
1167     android::emulation::AddressSpaceDeviceLoadResources asgLoadResources;
1168     for (const auto& [contextId, context] : mContexts) {
1169         for (const auto [resourceId, asgId] : context.AsgInstances()) {
1170             auto resourceIt = mResources.find(resourceId);
1171             if (resourceIt == mResources.end()) {
1172                 stream_renderer_error("Failed to restore ASG device: context %" PRIu32
1173                                       " claims resource %" PRIu32 " is used for ASG %" PRIu32
1174                                       " but resource not found.",
1175                                       contextId, resourceId, asgId);
1176                 return -1;
1177             }
1178             auto& resource = resourceIt->second;
1179 
1180             void* mappedAddr = nullptr;
1181             uint64_t mappedSize = 0;
1182 
1183             int ret = resource.Map(&mappedAddr, &mappedSize);
1184             if (ret) {
1185                 stream_renderer_error("Failed to restore ASG device: failed to map resource %" PRIu32, resourceId);
1186                 return -1;
1187             }
1188 
1189             asgLoadResources.contextExternalMemoryMap[asgId] = {
1190                 .externalAddress = mappedAddr,
1191                 .externalAddressSize = mappedSize,
1192             };
1193         }
1194     }
1195 
1196     int ret = android::emulation::goldfish_address_space_memory_state_set_load_resources(asgLoadResources);
1197     if (ret) {
1198         stream_renderer_error("Failed to restore ASG device: failed to set ASG load resources.");
1199         return ret;
1200     }
1201 
1202     ret = android::emulation::goldfish_address_space_memory_state_load(loadStream.stream);
1203     if (ret) {
1204         stream_renderer_error("Failed to restore ASG device: failed to restore ASG state.");
1205         return ret;
1206     }
1207     return 0;
1208 }
1209 
restore(const char * directory)1210 int VirtioGpuFrontend::restore(const char* directory) {
1211     stream_renderer_debug("directory:%s", directory);
1212 
1213     destroyVirtioGpuObjects();
1214 
1215     int ret = restoreRenderer(directory);
1216     if (ret) {
1217         stream_renderer_error("Failed to load snapshot: failed to load renderer.");
1218         return ret;
1219     }
1220 
1221     ret = restoreFrontend(directory);
1222     if (ret) {
1223         stream_renderer_error("Failed to load snapshot: failed to load frontend.");
1224         return ret;
1225     }
1226 
1227     ret = restoreAsg(directory);
1228     if (ret) {
1229         stream_renderer_error("Failed to load snapshot: failed to load ASG device.");
1230         return ret;
1231     }
1232 
1233     android_getOpenglesRenderer()->resumeAll();
1234 
1235     stream_renderer_debug("directory:%s - done!", directory);
1236     return 0;
1237 }
1238 
1239 #endif  // ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
1240 
1241 }  // namespace host
1242 }  // namespace gfxstream
1243