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