1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "RutabagaLayer.h"
18
19 #include <inttypes.h>
20 #include <log/log.h>
21 #include <string.h>
22
23 #include <algorithm>
24 #include <cstdlib>
25 #include <future>
26 #include <memory>
27 #include <optional>
28 #include <queue>
29 #include <sstream>
30 #include <string>
31 #include <thread>
32 #include <unordered_map>
33 #include <variant>
34
35 // Blueprint and Meson builds place things differently
36 #if defined(ANDROID)
37 #include "rutabaga_gfx_ffi.h"
38 #else
39 #include "rutabaga_gfx/rutabaga_gfx_ffi.h"
40 #endif
41
42 namespace gfxstream {
43 namespace {
44
45 constexpr const uint32_t kInvalidContextId = 0;
46
Split(const std::string & s,const std::string & delimiters)47 std::vector<std::string> Split(const std::string& s, const std::string& delimiters) {
48 if (delimiters.empty()) {
49 return {};
50 }
51
52 std::vector<std::string> result;
53
54 size_t base = 0;
55 size_t found;
56 while (true) {
57 found = s.find_first_of(delimiters, base);
58 result.push_back(s.substr(base, found - base));
59 if (found == s.npos) break;
60 base = found + 1;
61 }
62
63 return result;
64 }
65
Join(const std::vector<std::string> & things,const std::string & separator)66 std::string Join(const std::vector<std::string>& things, const std::string& separator) {
67 if (things.empty()) {
68 return "";
69 }
70
71 std::ostringstream result;
72 result << *things.begin();
73 for (auto it = std::next(things.begin()); it != things.end(); ++it) {
74 result << separator << *it;
75 }
76 return result.str();
77 }
78
79 } // namespace
80
81 class EmulatedVirtioGpu::EmulatedVirtioGpuImpl {
82 public:
83 EmulatedVirtioGpuImpl();
84 ~EmulatedVirtioGpuImpl();
85
86 bool Init(bool withGl, bool withVk, const std::string& features, EmulatedVirtioGpu* parent);
87
88 bool GetCaps(uint32_t capsetId, uint32_t guestCapsSize, uint8_t* capset);
89
90 std::optional<uint32_t> CreateContext(uint32_t contextInit);
91 void DestroyContext(uint32_t contextId);
92
93 uint8_t* Map(uint32_t resourceId);
94 void Unmap(uint32_t resourceId);
95
96 int SubmitCmd(uint32_t contextId, uint32_t cmdSize, void* cmd, uint32_t ringIdx,
97 VirtioGpuFenceFlags fenceFlags, uint32_t& fenceId,
98 std::optional<uint32_t> blobResourceId);
99
100 int Wait(uint32_t resourceId);
101
102 int TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
103 uint32_t transferSize);
104 int TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t x, uint32_t y,
105 uint32_t w, uint32_t h);
106
107 int TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
108 uint32_t transferSize);
109 int TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t x, uint32_t y, uint32_t w,
110 uint32_t h);
111
112 std::optional<uint32_t> CreateBlob(uint32_t contextId, uint32_t blobMem, uint32_t blobFlags,
113 uint64_t blobId, uint64_t blobSize);
114 std::optional<uint32_t> CreateVirglBlob(uint32_t contextId, uint32_t width, uint32_t height,
115 uint32_t virglFormat, uint32_t target, uint32_t bind,
116 uint32_t size);
117
118 void DestroyResource(uint32_t contextId, uint32_t resourceId);
119 void SnapshotSave(const std::string& directory);
120 void SnapshotRestore(const std::string& directory);
121
122 uint32_t CreateEmulatedFence();
123
124 void SignalEmulatedFence(uint32_t fenceId);
125
126 int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds);
127
128 private:
129 struct VirtioGpuTaskContextAttachResource {
130 uint32_t contextId;
131 uint32_t resourceId;
132 };
133 struct VirtioGpuTaskContextDetachResource {
134 uint32_t contextId;
135 uint32_t resourceId;
136 };
137 struct VirtioGpuTaskCreateContext {
138 uint32_t contextId;
139 uint32_t contextInit;
140 std::string contextName;
141 };
142 struct VirtioGpuTaskCreateBlob {
143 uint32_t contextId;
144 uint32_t resourceId;
145 struct rutabaga_create_blob params;
146 };
147 struct VirtioGpuTaskCreateResource {
148 uint32_t contextId;
149 uint32_t resourceId;
150 uint8_t* resourceBytes;
151 struct rutabaga_create_3d params;
152 };
153 struct VirtioGpuTaskDestroyContext {
154 uint32_t contextId;
155 };
156 struct VirtioGpuTaskMap {
157 uint32_t resourceId;
158 std::promise<uint8_t*> resourceMappedPromise;
159 };
160 struct VirtioGpuTaskUnmap {
161 uint32_t resourceId;
162 };
163 struct VirtioGpuTaskSubmitCmd {
164 uint32_t contextId;
165 std::vector<std::byte> commandBuffer;
166 };
167 struct VirtioGpuTaskTransferToHost {
168 uint32_t contextId;
169 uint32_t resourceId;
170 uint32_t x;
171 uint32_t y;
172 uint32_t w;
173 uint32_t h;
174 };
175 struct VirtioGpuTaskTransferFromHost {
176 uint32_t contextId;
177 uint32_t resourceId;
178 uint32_t x;
179 uint32_t y;
180 uint32_t w;
181 uint32_t h;
182 };
183 struct VirtioGpuTaskUnrefResource {
184 uint32_t resourceId;
185 };
186 struct VirtioGpuTaskSnapshotSave {
187 std::string directory;
188 };
189 struct VirtioGpuTaskSnapshotRestore {
190 std::string directory;
191 };
192 using VirtioGpuTask =
193 std::variant<VirtioGpuTaskContextAttachResource, VirtioGpuTaskContextDetachResource,
194 VirtioGpuTaskCreateBlob, VirtioGpuTaskCreateContext,
195 VirtioGpuTaskCreateResource, VirtioGpuTaskDestroyContext, VirtioGpuTaskMap,
196 VirtioGpuTaskUnmap, VirtioGpuTaskSubmitCmd, VirtioGpuTaskTransferFromHost,
197 VirtioGpuTaskTransferToHost, VirtioGpuTaskUnrefResource,
198 VirtioGpuTaskSnapshotSave, VirtioGpuTaskSnapshotRestore>;
199
200 struct VirtioGpuFence {
201 uint32_t fenceId;
202 uint32_t ringIdx;
203 };
204
205 struct VirtioGpuTaskWithWaitable {
206 uint32_t contextId;
207 VirtioGpuTask task;
208 std::promise<void> taskCompletedSignaler;
209 std::optional<VirtioGpuFence> fence;
210 };
211
212 std::shared_future<void> EnqueueVirtioGpuTask(
213 uint32_t contextId, VirtioGpuTask task, std::optional<VirtioGpuFence> fence = std::nullopt);
214 void DoTask(VirtioGpuTaskContextAttachResource task);
215 void DoTask(VirtioGpuTaskContextDetachResource task);
216 void DoTask(VirtioGpuTaskCreateContext task);
217 void DoTask(VirtioGpuTaskCreateBlob task);
218 void DoTask(VirtioGpuTaskCreateResource task);
219 void DoTask(VirtioGpuTaskDestroyContext task);
220 void DoTask(VirtioGpuTaskMap task);
221 void DoTask(VirtioGpuTaskUnmap task);
222 void DoTask(VirtioGpuTaskSubmitCmd task);
223 void DoTask(VirtioGpuTaskTransferFromHost task);
224 void DoTask(VirtioGpuTaskTransferToHost task);
225 void DoTask(VirtioGpuTaskWithWaitable task);
226 void DoTask(VirtioGpuTaskUnrefResource task);
227 void DoTask(VirtioGpuTaskSnapshotSave task);
228 void DoTask(VirtioGpuTaskSnapshotRestore task);
229
230 void RunVirtioGpuTaskProcessingLoop();
231
232 std::atomic<uint32_t> mNextContextId{1};
233 std::atomic<uint32_t> mNextVirtioGpuResourceId{1};
234 std::atomic<uint32_t> mNextVirtioGpuFenceId{1};
235
236 std::atomic_bool mShuttingDown{false};
237
238 std::mutex mTasksMutex;
239 std::queue<VirtioGpuTaskWithWaitable> mTasks;
240
241 enum class EmulatedResourceType {
242 kBlob,
243 kPipe,
244 };
245 struct EmulatedResource {
246 EmulatedResourceType type;
247
248 std::mutex pendingWaitablesMutex;
249 std::vector<std::shared_future<void>> pendingWaitables;
250
251 // For non-blob resources, the guest shadow memory.
252 std::unique_ptr<uint8_t[]> guestBytes;
253
254 // For mappable blob resources, the host memory once it is mapped.
255 std::shared_future<uint8_t*> mappedHostBytes;
256
257 // For resources with iovecs. Test layer just needs 1.
258 struct iovec iovec;
259 };
260 std::mutex mResourcesMutex;
261 std::unordered_map<uint32_t, EmulatedResource> mResources;
262
CreateResource(uint32_t resourceId,EmulatedResourceType resourceType)263 EmulatedResource* CreateResource(uint32_t resourceId, EmulatedResourceType resourceType) {
264 std::lock_guard<std::mutex> lock(mResourcesMutex);
265
266 auto [it, created] = mResources.emplace(
267 std::piecewise_construct, std::forward_as_tuple(resourceId), std::forward_as_tuple());
268 if (!created) {
269 ALOGE("Created resource %" PRIu32 " twice?", resourceId);
270 }
271
272 EmulatedResource* resource = &it->second;
273 resource->type = resourceType;
274 return resource;
275 }
276
GetResource(uint32_t resourceId)277 EmulatedResource* GetResource(uint32_t resourceId) {
278 std::lock_guard<std::mutex> lock(mResourcesMutex);
279
280 auto it = mResources.find(resourceId);
281 if (it == mResources.end()) {
282 return nullptr;
283 }
284
285 return &it->second;
286 }
287
DeleteResource(uint32_t resourceId)288 void DeleteResource(uint32_t resourceId) {
289 std::lock_guard<std::mutex> lock(mResourcesMutex);
290 mResources.erase(resourceId);
291 }
292
293 struct EmulatedFence {
294 std::promise<void> signaler;
295 std::shared_future<void> waitable;
296 };
297 std::mutex mVirtioGpuFencesMutex;
298 std::unordered_map<uint32_t, EmulatedFence> mVirtioGpuFences;
299
300 std::thread mWorkerThread;
301 struct rutabaga* mRutabaga = nullptr;
302 std::unordered_map<uint32_t, std::vector<uint8_t>> mCapsets;
303 };
304
EmulatedVirtioGpuImpl()305 EmulatedVirtioGpu::EmulatedVirtioGpuImpl::EmulatedVirtioGpuImpl()
306 : mWorkerThread([this]() { RunVirtioGpuTaskProcessingLoop(); }) {}
307
~EmulatedVirtioGpuImpl()308 EmulatedVirtioGpu::EmulatedVirtioGpuImpl::~EmulatedVirtioGpuImpl() {
309 mShuttingDown = true;
310 mWorkerThread.join();
311
312 if (mRutabaga) {
313 rutabaga_finish(&mRutabaga);
314 mRutabaga = nullptr;
315 }
316 }
317
318 namespace {
319
WriteFenceTrampoline(uint64_t cookie,const struct rutabaga_fence * fence)320 void WriteFenceTrampoline(uint64_t cookie, const struct rutabaga_fence* fence) {
321 auto* gpu = reinterpret_cast<EmulatedVirtioGpu*>(cookie);
322 gpu->SignalEmulatedFence(fence->fence_id);
323 }
324
325 } // namespace
326
Init(bool withGl,bool withVk,const std::string & features,EmulatedVirtioGpu * parent)327 bool EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Init(bool withGl, bool withVk,
328 const std::string& features,
329 EmulatedVirtioGpu* parent) {
330 int32_t ret;
331 uint64_t capsetMask = 0;
332 uint32_t numCapsets = 0;
333 struct rutabaga_builder builder = {0};
334
335 if (withGl) {
336 capsetMask |= (1 << RUTABAGA_CAPSET_GFXSTREAM_GLES);
337 }
338
339 if (withVk) {
340 capsetMask |= (1 << RUTABAGA_CAPSET_GFXSTREAM_VULKAN);
341 }
342
343 builder.user_data = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(parent)),
344 builder.fence_cb = WriteFenceTrampoline;
345 builder.capset_mask = capsetMask;
346 builder.wsi = RUTABAGA_WSI_SURFACELESS;
347 if (!features.empty()) {
348 builder.renderer_features = features.c_str();
349 }
350
351 ret = rutabaga_init(&builder, &mRutabaga);
352 if (ret) {
353 ALOGE("Failed to initialize rutabaga");
354 return false;
355 }
356
357 ret = rutabaga_get_num_capsets(mRutabaga, &numCapsets);
358 if (ret) {
359 ALOGE("Failed to get number of capsets");
360 return false;
361 }
362
363 for (uint32_t i = 0; i < numCapsets; i++) {
364 uint32_t capsetId, capsetVersion, capsetSize;
365 std::vector<uint8_t> capsetData;
366
367 ret = rutabaga_get_capset_info(mRutabaga, i, &capsetId, &capsetVersion, &capsetSize);
368 if (ret) {
369 ALOGE("Failed to get capset info");
370 return false;
371 }
372
373 capsetData.resize(capsetSize);
374
375 ret = rutabaga_get_capset(mRutabaga, capsetId, capsetVersion, (uint8_t*)capsetData.data(),
376 capsetSize);
377
378 if (ret) {
379 ALOGE("Failed to get capset");
380 return false;
381 }
382
383 mCapsets[capsetId] = capsetData;
384 }
385
386 return true;
387 }
388
GetCaps(uint32_t capsetId,uint32_t guestCapsSize,uint8_t * capset)389 bool EmulatedVirtioGpu::EmulatedVirtioGpuImpl::GetCaps(uint32_t capsetId, uint32_t guestCapsSize,
390 uint8_t* capset) {
391 if (mCapsets.count(capsetId) == 0) return false;
392
393 auto capsetData = mCapsets[capsetId];
394 auto copySize = std::min(static_cast<size_t>(guestCapsSize), capsetData.size());
395 memcpy(capset, capsetData.data(), copySize);
396
397 return true;
398 }
399
CreateContext(uint32_t contextInit)400 std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateContext(
401 uint32_t contextInit) {
402 const uint32_t contextId = mNextContextId++;
403
404 VirtioGpuTaskCreateContext task = {
405 .contextId = contextId,
406 .contextInit = contextInit,
407 .contextName = "EmulatedVirtioGpu Context " + std::to_string(contextId),
408 };
409 EnqueueVirtioGpuTask(contextId, std::move(task));
410 return contextId;
411 }
412
DestroyContext(uint32_t contextId)413 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DestroyContext(uint32_t contextId) {
414 VirtioGpuTaskDestroyContext task = {
415 .contextId = contextId,
416 };
417 EnqueueVirtioGpuTask(contextId, std::move(task));
418 }
419
Map(uint32_t resourceId)420 uint8_t* EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Map(uint32_t resourceId) {
421 EmulatedResource* resource = GetResource(resourceId);
422 if (resource == nullptr) {
423 ALOGE("Failed to Map() resource %" PRIu32 ": not found.", resourceId);
424 return nullptr;
425 }
426
427 uint8_t* mapped = nullptr;
428 if (resource->type == EmulatedResourceType::kBlob) {
429 if (!resource->mappedHostBytes.valid()) {
430 ALOGE("Failed to Map() resource %" PRIu32
431 ": attempting to map blob "
432 "without mappable flag?",
433 resourceId);
434 return nullptr;
435 }
436 mapped = resource->mappedHostBytes.get();
437 } else if (resource->type == EmulatedResourceType::kPipe) {
438 mapped = resource->guestBytes.get();
439 }
440 return mapped;
441 }
442
Unmap(uint32_t resourceId)443 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Unmap(uint32_t resourceId) {
444 VirtioGpuTaskUnmap task = {
445 .resourceId = resourceId,
446 };
447 EnqueueVirtioGpuTask(0, std::move(task));
448 }
449
Wait(uint32_t resourceId)450 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Wait(uint32_t resourceId) {
451 EmulatedResource* resource = GetResource(resourceId);
452 if (resource == nullptr) {
453 ALOGE("Failed to Wait() on resource %" PRIu32 ": not found.", resourceId);
454 return -1;
455 }
456
457 std::vector<std::shared_future<void>> pendingWaitables;
458 {
459 std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
460 pendingWaitables = resource->pendingWaitables;
461 resource->pendingWaitables.clear();
462 }
463 for (auto& waitable : pendingWaitables) {
464 waitable.wait();
465 }
466
467 return 0;
468 }
469
TransferFromHost(uint32_t contextId,uint32_t resourceId,uint32_t transferOffset,uint32_t transferSize)470 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferFromHost(uint32_t contextId,
471 uint32_t resourceId,
472 uint32_t transferOffset,
473 uint32_t transferSize) {
474 return TransferFromHost(contextId, resourceId, transferOffset, 0, transferSize, 1);
475 }
476
TransferFromHost(uint32_t contextId,uint32_t resourceId,uint32_t x,uint32_t y,uint32_t w,uint32_t h)477 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferFromHost(uint32_t contextId,
478 uint32_t resourceId, uint32_t x,
479 uint32_t y, uint32_t w, uint32_t h) {
480 EmulatedResource* resource = GetResource(resourceId);
481 if (resource == nullptr) {
482 ALOGE("Failed to TransferFromHost() on resource %" PRIu32 ": not found.", resourceId);
483 return -1;
484 }
485
486 VirtioGpuTaskTransferFromHost task = {
487 .contextId = contextId,
488 .resourceId = resourceId,
489 .x = x,
490 .y = y,
491 .w = w,
492 .h = h,
493 };
494 auto waitable = EnqueueVirtioGpuTask(contextId, std::move(task));
495
496 {
497 std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
498 resource->pendingWaitables.push_back(std::move(waitable));
499 }
500
501 return 0;
502 }
503
TransferToHost(uint32_t contextId,uint32_t resourceId,uint32_t transferOffset,uint32_t transferSize)504 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferToHost(uint32_t contextId,
505 uint32_t resourceId,
506 uint32_t transferOffset,
507 uint32_t transferSize) {
508 return TransferToHost(contextId, resourceId, transferOffset, 0, transferSize, 1);
509 }
510
TransferToHost(uint32_t contextId,uint32_t resourceId,uint32_t x,uint32_t y,uint32_t w,uint32_t h)511 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferToHost(uint32_t contextId,
512 uint32_t resourceId, uint32_t x,
513 uint32_t y, uint32_t w, uint32_t h) {
514 EmulatedResource* resource = GetResource(resourceId);
515 if (resource == nullptr) {
516 ALOGE("Failed to TransferFromHost() on resource %" PRIu32 ": not found.", resourceId);
517 return -1;
518 }
519
520 VirtioGpuTaskTransferToHost task = {
521 .contextId = contextId,
522 .resourceId = resourceId,
523 .x = x,
524 .y = y,
525 .w = w,
526 .h = h,
527 };
528 auto waitable = EnqueueVirtioGpuTask(contextId, std::move(task));
529
530 {
531 std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
532 resource->pendingWaitables.push_back(std::move(waitable));
533 }
534
535 return 0;
536 }
537
CreateBlob(uint32_t contextId,uint32_t blobMem,uint32_t blobFlags,uint64_t blobId,uint64_t blobSize)538 std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateBlob(
539 uint32_t contextId, uint32_t blobMem, uint32_t blobFlags, uint64_t blobId, uint64_t blobSize) {
540 const uint32_t resourceId = mNextVirtioGpuResourceId++;
541
542 ALOGV("Enquing task to create blob resource-id:%d size:%" PRIu64, resourceId, blobSize);
543
544 EmulatedResource* resource = CreateResource(resourceId, EmulatedResourceType::kBlob);
545
546 VirtioGpuTaskCreateBlob createTask{
547 .contextId = contextId,
548 .resourceId = resourceId,
549 .params =
550 {
551 .blob_mem = blobMem,
552 .blob_flags = blobFlags,
553 .blob_id = blobId,
554 .size = blobSize,
555 },
556 };
557 auto createBlobCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(createTask));
558 resource->pendingWaitables.push_back(std::move(createBlobCompletedWaitable));
559
560 if (blobFlags & 0x001 /*kBlobFlagMappable*/) {
561 std::promise<uint8_t*> mappedBytesPromise;
562 std::shared_future<uint8_t*> mappedBytesWaitable = mappedBytesPromise.get_future();
563
564 VirtioGpuTaskMap mapTask{
565 .resourceId = resourceId,
566 .resourceMappedPromise = std::move(mappedBytesPromise),
567 };
568 EnqueueVirtioGpuTask(contextId, std::move(mapTask));
569 resource->mappedHostBytes = std::move(mappedBytesWaitable);
570 }
571
572 VirtioGpuTaskContextAttachResource attachTask{
573 .contextId = contextId,
574 .resourceId = resourceId,
575 };
576 EnqueueVirtioGpuTask(contextId, std::move(attachTask));
577
578 return resourceId;
579 }
580
CreateVirglBlob(uint32_t contextId,uint32_t width,uint32_t height,uint32_t virglFormat,uint32_t target,uint32_t bind,uint32_t size)581 std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateVirglBlob(
582 uint32_t contextId, uint32_t width, uint32_t height, uint32_t virglFormat, uint32_t target,
583 uint32_t bind, uint32_t size) {
584 const uint32_t resourceId = mNextVirtioGpuResourceId++;
585
586 EmulatedResource* resource = CreateResource(resourceId, EmulatedResourceType::kPipe);
587
588 resource->guestBytes = std::make_unique<uint8_t[]>(size);
589
590 VirtioGpuTaskCreateResource task{
591 .contextId = contextId,
592 .resourceId = resourceId,
593 .resourceBytes = resource->guestBytes.get(),
594 .params =
595 {
596 .target = target,
597 .format = virglFormat,
598 .bind = bind,
599 .width = width,
600 .height = height,
601 .depth = 1,
602 .array_size = 1,
603 .last_level = 0,
604 .nr_samples = 0,
605 .flags = 0,
606 },
607 };
608 auto taskCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(task));
609 resource->pendingWaitables.push_back(std::move(taskCompletedWaitable));
610
611 VirtioGpuTaskContextAttachResource attachTask{
612 .contextId = contextId,
613 .resourceId = resourceId,
614 };
615 EnqueueVirtioGpuTask(contextId, std::move(attachTask));
616
617 return resourceId;
618 }
619
DestroyResource(uint32_t contextId,uint32_t resourceId)620 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DestroyResource(uint32_t contextId,
621 uint32_t resourceId) {
622 DeleteResource(resourceId);
623
624 VirtioGpuTaskUnrefResource unrefTask{
625 .resourceId = resourceId,
626 };
627 EnqueueVirtioGpuTask(contextId, std::move(unrefTask));
628
629 VirtioGpuTaskContextDetachResource detachTask{
630 .contextId = contextId,
631 .resourceId = resourceId,
632 };
633 EnqueueVirtioGpuTask(contextId, std::move(detachTask));
634 }
635
SnapshotSave(const std::string & directory)636 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::SnapshotSave(const std::string& directory) {
637 uint32_t contextId = 0;
638
639 VirtioGpuTaskSnapshotSave saveTask{
640 .directory = directory,
641 };
642 EnqueueVirtioGpuTask(contextId, std::move(saveTask)).wait();
643 }
644
SnapshotRestore(const std::string & directory)645 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::SnapshotRestore(const std::string& directory) {
646 uint32_t contextId = 0;
647
648 VirtioGpuTaskSnapshotRestore restoreTask{
649 .directory = directory,
650 };
651
652 EnqueueVirtioGpuTask(contextId, std::move(restoreTask)).wait();
653 }
654
SubmitCmd(uint32_t contextId,uint32_t cmdSize,void * cmd,uint32_t ringIdx,VirtioGpuFenceFlags fenceFlags,uint32_t & fenceId,std::optional<uint32_t> blobResourceId)655 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::SubmitCmd(uint32_t contextId, uint32_t cmdSize,
656 void* cmd, uint32_t ringIdx,
657 VirtioGpuFenceFlags fenceFlags,
658 uint32_t& fenceId,
659 std::optional<uint32_t> blobResourceId) {
660 std::optional<VirtioGpuFence> fence;
661 if (fenceFlags & kFlagFence) {
662 fence = VirtioGpuFence{.fenceId = CreateEmulatedFence(), .ringIdx = ringIdx};
663 }
664
665 const VirtioGpuTaskSubmitCmd task = {
666 .contextId = contextId,
667 .commandBuffer = std::vector<std::byte>(reinterpret_cast<std::byte*>(cmd),
668 reinterpret_cast<std::byte*>(cmd) + cmdSize),
669 };
670 auto taskCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(task), fence);
671
672 if (blobResourceId) {
673 EmulatedResource* resource = GetResource(*blobResourceId);
674 if (resource == nullptr) {
675 ALOGE("Failed to SubmitCmd() with resource %" PRIu32 ": not found.", *blobResourceId);
676 return -1;
677 }
678
679 {
680 std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
681 resource->pendingWaitables.push_back(std::move(taskCompletedWaitable));
682 }
683 }
684
685 if (fenceFlags & kFlagFence) {
686 fenceId = (*fence).fenceId;
687 }
688
689 return 0;
690 }
691
WaitOnEmulatedFence(int fenceAsFileDescriptor,int timeoutMilliseconds)692 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::WaitOnEmulatedFence(int fenceAsFileDescriptor,
693 int timeoutMilliseconds) {
694 uint32_t fenceId = static_cast<uint32_t>(fenceAsFileDescriptor);
695 ALOGV("Waiting on fence:%d", (int)fenceId);
696
697 std::shared_future<void> waitable;
698
699 {
700 std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex);
701
702 auto fenceIt = mVirtioGpuFences.find(fenceId);
703 if (fenceIt == mVirtioGpuFences.end()) {
704 ALOGE("Fence:%d already signaled", (int)fenceId);
705 return 0;
706 }
707 auto& fence = fenceIt->second;
708
709 waitable = fence.waitable;
710 }
711
712 auto status = waitable.wait_for(std::chrono::milliseconds(timeoutMilliseconds));
713 if (status == std::future_status::ready) {
714 ALOGV("Finished waiting for fence:%d", (int)fenceId);
715 return 0;
716 } else {
717 ALOGE("Timed out waiting for fence:%d", (int)fenceId);
718 return -1;
719 }
720 }
721
SignalEmulatedFence(uint32_t fenceId)722 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::SignalEmulatedFence(uint32_t fenceId) {
723 ALOGV("Signaling fence:%d", (int)fenceId);
724
725 std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex);
726
727 auto fenceIt = mVirtioGpuFences.find(fenceId);
728 if (fenceIt == mVirtioGpuFences.end()) {
729 ALOGE("Failed to find fence %" PRIu32, fenceId);
730 return;
731 }
732 auto& fenceInfo = fenceIt->second;
733 fenceInfo.signaler.set_value();
734 }
735
CreateEmulatedFence()736 uint32_t EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateEmulatedFence() {
737 const uint32_t fenceId = mNextVirtioGpuFenceId++;
738
739 ALOGV("Creating fence:%d", (int)fenceId);
740
741 std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex);
742
743 auto [fenceIt, fenceCreated] = mVirtioGpuFences.emplace(fenceId, EmulatedFence{});
744 if (!fenceCreated) {
745 ALOGE("Attempting to recreate fence %" PRIu32, fenceId);
746 }
747
748 auto& fenceInfo = fenceIt->second;
749 fenceInfo.waitable = fenceInfo.signaler.get_future();
750
751 return fenceId;
752 }
753
EnqueueVirtioGpuTask(uint32_t contextId,VirtioGpuTask task,std::optional<VirtioGpuFence> fence)754 std::shared_future<void> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::EnqueueVirtioGpuTask(
755 uint32_t contextId, VirtioGpuTask task, std::optional<VirtioGpuFence> fence) {
756 std::promise<void> taskCompletedSignaler;
757 std::shared_future<void> taskCompletedWaitable(taskCompletedSignaler.get_future());
758
759 std::lock_guard<std::mutex> lock(mTasksMutex);
760 mTasks.push(VirtioGpuTaskWithWaitable{
761 .contextId = contextId,
762 .task = std::move(task),
763 .taskCompletedSignaler = std::move(taskCompletedSignaler),
764 .fence = fence,
765 });
766
767 return taskCompletedWaitable;
768 }
769
DoTask(VirtioGpuTaskContextAttachResource task)770 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskContextAttachResource task) {
771 ALOGV("Performing task to attach resource-id:%d to context-id:%d", task.resourceId,
772 task.contextId);
773
774 rutabaga_context_attach_resource(mRutabaga, task.contextId, task.resourceId);
775
776 ALOGV("Performing task to attach resource-id:%d to context-id:%d - done", task.resourceId,
777 task.contextId);
778 }
779
DoTask(VirtioGpuTaskContextDetachResource task)780 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskContextDetachResource task) {
781 ALOGV("Performing task to detach resource-id:%d to context-id:%d", task.resourceId,
782 task.contextId);
783
784 rutabaga_context_detach_resource(mRutabaga, task.contextId, task.resourceId);
785
786 ALOGV("Performing task to detach resource-id:%d to context-id:%d - done", task.resourceId,
787 task.contextId);
788 }
789
DoTask(VirtioGpuTaskCreateBlob task)790 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateBlob task) {
791 ALOGV("Performing task to create blob resource-id:%d", task.resourceId);
792
793 int ret =
794 rutabaga_resource_create_blob(mRutabaga, task.contextId, task.resourceId, &task.params,
795 /*iovecs=*/nullptr,
796 /*handle=*/nullptr);
797 if (ret) {
798 ALOGE("Failed to create blob.");
799 }
800
801 ALOGV("Performing task to create blob resource-id:%d - done", task.resourceId);
802 }
803
DoTask(VirtioGpuTaskCreateContext task)804 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateContext task) {
805 ALOGV("Performing task to create context-id:%" PRIu32 " context-init:%" PRIu32
806 " context-name:%s",
807 task.contextId, task.contextInit, task.contextName.c_str());
808
809 int ret = rutabaga_context_create(mRutabaga, task.contextId, task.contextName.size(),
810 task.contextName.data(), task.contextInit);
811 if (ret) {
812 ALOGE("Failed to create context-id:%" PRIu32 ".", task.contextId);
813 return;
814 }
815
816 ALOGV("Performing task to create context-id:%" PRIu32 " context-init:%" PRIu32
817 " context-name:%s - done",
818 task.contextId, task.contextInit, task.contextName.c_str());
819 }
820
DoTask(VirtioGpuTaskCreateResource task)821 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateResource task) {
822 ALOGV("Performing task to create resource resource:%d", task.resourceId);
823
824 EmulatedResource* resource = GetResource(task.resourceId);
825
826 int ret = rutabaga_resource_create_3d(mRutabaga, task.resourceId, &task.params);
827 if (ret) {
828 ALOGE("Failed to create resource:%d", task.resourceId);
829 }
830
831 resource->iovec.iov_base = task.resourceBytes;
832 resource->iovec.iov_len = task.params.width * task.params.height * 4;
833
834 struct rutabaga_iovecs vecs = {0};
835 vecs.iovecs = &resource->iovec;
836 vecs.num_iovecs = 1;
837
838 ret = rutabaga_resource_attach_backing(mRutabaga, task.resourceId, &vecs);
839 if (ret) {
840 ALOGE("Failed to attach iov to resource:%d", task.resourceId);
841 }
842
843 ALOGV("Performing task to create resource resource:%d - done", task.resourceId);
844
845 rutabaga_context_attach_resource(mRutabaga, task.contextId, task.resourceId);
846 }
847
DoTask(VirtioGpuTaskDestroyContext task)848 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskDestroyContext task) {
849 ALOGV("Performing task to destroy context-id:%" PRIu32, task.contextId);
850
851 rutabaga_context_destroy(mRutabaga, task.contextId);
852
853 ALOGV("Performing task to destroy context-id:%" PRIu32 " - done", task.contextId);
854 }
855
DoTask(VirtioGpuTaskMap task)856 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskMap task) {
857 ALOGV("Performing task to map resource resource:%d", task.resourceId);
858
859 struct rutabaga_mapping mapping;
860 int ret = rutabaga_resource_map(mRutabaga, task.resourceId, &mapping);
861 if (ret) {
862 ALOGE("Failed to map resource:%d", task.resourceId);
863 return;
864 }
865
866 task.resourceMappedPromise.set_value(reinterpret_cast<uint8_t*>(mapping.ptr));
867 ALOGV("Performing task to map resource resource:%d - done", task.resourceId);
868 }
869
DoTask(VirtioGpuTaskUnmap task)870 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskUnmap task) {
871 ALOGV("Performing task to unmap resource resource:%d", task.resourceId);
872 rutabaga_resource_unmap(mRutabaga, task.resourceId);
873 ALOGV("Performing task to unmap resource:%d - done", task.resourceId);
874 }
875
DoTask(VirtioGpuTaskSubmitCmd task)876 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskSubmitCmd task) {
877 ALOGV("Performing task to execbuffer");
878
879 if (task.commandBuffer.size() % 4 != 0) {
880 ALOGE("Unaligned command buffer?");
881 return;
882 }
883
884 struct rutabaga_command cmd = {
885 .ctx_id = task.contextId,
886 .cmd_size = static_cast<uint32_t>(task.commandBuffer.size()),
887 .cmd = reinterpret_cast<uint8_t*>(task.commandBuffer.data()),
888 .num_in_fences = 0,
889 .fence_ids = nullptr,
890 };
891
892 int ret = rutabaga_submit_command(mRutabaga, &cmd);
893 if (ret) {
894 ALOGE("Failed to execbuffer.");
895 }
896
897 ALOGV("Performing task to execbuffer - done");
898 }
899
DoTask(VirtioGpuTaskTransferFromHost task)900 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskTransferFromHost task) {
901 struct rutabaga_transfer transfer = {0};
902 transfer.x = task.x;
903 transfer.y = task.y;
904 transfer.z = 0;
905 transfer.w = task.w;
906 transfer.h = task.h;
907 transfer.d = 1;
908
909 int ret = rutabaga_resource_transfer_read(mRutabaga, task.contextId, task.resourceId, &transfer,
910 nullptr);
911 if (ret) {
912 ALOGE("Failed to transferFromHost() for resource:%" PRIu32, task.resourceId);
913 }
914 }
915
DoTask(VirtioGpuTaskTransferToHost task)916 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskTransferToHost task) {
917 struct rutabaga_transfer transfer = {0};
918 transfer.x = task.x;
919 transfer.y = task.y;
920 transfer.z = 0;
921 transfer.w = task.w;
922 transfer.h = task.h;
923 transfer.d = 1;
924
925 int ret =
926 rutabaga_resource_transfer_write(mRutabaga, task.contextId, task.resourceId, &transfer);
927 if (ret) {
928 ALOGE("Failed to transferToHost() for resource:%" PRIu32, task.resourceId);
929 }
930 }
931
DoTask(VirtioGpuTaskUnrefResource task)932 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskUnrefResource task) {
933 rutabaga_resource_unref(mRutabaga, task.resourceId);
934 }
935
DoTask(VirtioGpuTaskSnapshotSave task)936 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskSnapshotSave task) {
937 int ret = rutabaga_snapshot(mRutabaga, task.directory.c_str());
938 if (ret) {
939 ALOGE("snapshotting failed");
940 }
941 }
942
DoTask(VirtioGpuTaskSnapshotRestore task)943 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskSnapshotRestore task) {
944 int ret = rutabaga_restore(mRutabaga, task.directory.c_str());
945 if (ret) {
946 ALOGE("snapshot restore failed");
947 }
948 }
949
DoTask(VirtioGpuTaskWithWaitable task)950 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskWithWaitable task) {
951 std::visit(
952 [this](auto&& work) {
953 using T = std::decay_t<decltype(work)>;
954 if constexpr (std::is_same_v<T, VirtioGpuTaskContextAttachResource>) {
955 DoTask(std::move(work));
956 } else if constexpr (std::is_same_v<T, VirtioGpuTaskContextDetachResource>) {
957 DoTask(std::move(work));
958 } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateBlob>) {
959 DoTask(std::move(work));
960 } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateContext>) {
961 DoTask(std::move(work));
962 } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateResource>) {
963 DoTask(std::move(work));
964 } else if constexpr (std::is_same_v<T, VirtioGpuTaskDestroyContext>) {
965 DoTask(std::move(work));
966 } else if constexpr (std::is_same_v<T, VirtioGpuTaskMap>) {
967 DoTask(std::move(work));
968 } else if constexpr (std::is_same_v<T, VirtioGpuTaskUnmap>) {
969 DoTask(std::move(work));
970 } else if constexpr (std::is_same_v<T, VirtioGpuTaskSubmitCmd>) {
971 DoTask(std::move(work));
972 } else if constexpr (std::is_same_v<T, VirtioGpuTaskTransferFromHost>) {
973 DoTask(std::move(work));
974 } else if constexpr (std::is_same_v<T, VirtioGpuTaskTransferToHost>) {
975 DoTask(std::move(work));
976 } else if constexpr (std::is_same_v<T, VirtioGpuTaskUnrefResource>) {
977 DoTask(std::move(work));
978 } else if constexpr (std::is_same_v<T, VirtioGpuTaskSnapshotSave>) {
979 DoTask(std::move(work));
980 } else if constexpr (std::is_same_v<T, VirtioGpuTaskSnapshotRestore>) {
981 DoTask(std::move(work));
982 }
983 },
984 task.task);
985
986 if (task.fence) {
987 const struct rutabaga_fence fenceInfo = {
988 .flags = RUTABAGA_FLAG_INFO_RING_IDX,
989 .fence_id = (*task.fence).fenceId,
990 .ctx_id = task.contextId,
991 .ring_idx = (*task.fence).ringIdx,
992 };
993 int ret = rutabaga_create_fence(mRutabaga, &fenceInfo);
994 if (ret) {
995 ALOGE("Failed to create fence.");
996 }
997 }
998
999 task.taskCompletedSignaler.set_value();
1000 }
1001
RunVirtioGpuTaskProcessingLoop()1002 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::RunVirtioGpuTaskProcessingLoop() {
1003 while (!mShuttingDown.load()) {
1004 std::optional<VirtioGpuTaskWithWaitable> task;
1005
1006 {
1007 std::lock_guard<std::mutex> lock(mTasksMutex);
1008 if (!mTasks.empty()) {
1009 task = std::move(mTasks.front());
1010 mTasks.pop();
1011 }
1012 }
1013
1014 if (task) {
1015 DoTask(std::move(*task));
1016 }
1017 }
1018 }
1019
EmulatedVirtioGpu()1020 EmulatedVirtioGpu::EmulatedVirtioGpu()
1021 : mImpl{std::make_unique<EmulatedVirtioGpu::EmulatedVirtioGpuImpl>()} {}
1022
1023 namespace {
1024
1025 static std::mutex sInstanceMutex;
1026 static std::weak_ptr<EmulatedVirtioGpu> sInstance;
1027
1028 } // namespace
1029
1030 /*static*/
Get()1031 std::shared_ptr<EmulatedVirtioGpu> EmulatedVirtioGpu::Get() {
1032 std::lock_guard<std::mutex> lock(sInstanceMutex);
1033
1034 std::shared_ptr<EmulatedVirtioGpu> instance = sInstance.lock();
1035 if (instance != nullptr) {
1036 return instance;
1037 }
1038
1039 instance = std::shared_ptr<EmulatedVirtioGpu>(new EmulatedVirtioGpu());
1040
1041 bool withGl = false;
1042 bool withVk = false;
1043 std::string features;
1044
1045 struct BoolOption {
1046 std::string env;
1047 bool* val;
1048 };
1049 const std::vector<BoolOption> options = {
1050 {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl},
1051 {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk},
1052 };
1053 for (const BoolOption& option : options) {
1054 const char* val = std::getenv(option.env.c_str());
1055 if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) {
1056 *option.val = true;
1057 }
1058 }
1059
1060 struct StringOption {
1061 std::string env;
1062 std::string* val;
1063 };
1064 const std::vector<StringOption> stringOptions = {
1065 {"GFXSTREAM_EMULATED_VIRTIO_GPU_RENDERER_FEATURES", &features},
1066 };
1067 for (const StringOption& option : stringOptions) {
1068 const char* val = std::getenv(option.env.c_str());
1069 if (val != nullptr) {
1070 *option.val = std::string(val);
1071 }
1072 }
1073
1074 ALOGE("Initializing withGl:%d withVk:%d features:%s", withGl, withVk, features.c_str());
1075 if (!instance->Init(withGl, withVk, features)) {
1076 ALOGE("Failed to initialize EmulatedVirtioGpu.");
1077 return nullptr;
1078 }
1079 ALOGE("Successfully initialized EmulatedVirtioGpu.");
1080 sInstance = instance;
1081 return instance;
1082 }
1083
1084 /*static*/
GetNumActiveUsers()1085 uint32_t EmulatedVirtioGpu::GetNumActiveUsers() {
1086 std::lock_guard<std::mutex> lock(sInstanceMutex);
1087 std::shared_ptr<EmulatedVirtioGpu> instance = sInstance.lock();
1088 return instance.use_count();
1089 }
1090
Init(bool withGl,bool withVk,const std::string & features)1091 bool EmulatedVirtioGpu::Init(bool withGl, bool withVk, const std::string& features) {
1092 return mImpl->Init(withGl, withVk, features, this);
1093 }
1094
CreateContext(uint32_t contextInit)1095 std::optional<uint32_t> EmulatedVirtioGpu::CreateContext(uint32_t contextInit) {
1096 return mImpl->CreateContext(contextInit);
1097 }
1098
DestroyContext(uint32_t contextId)1099 void EmulatedVirtioGpu::DestroyContext(uint32_t contextId) { mImpl->DestroyContext(contextId); }
1100
GetCaps(uint32_t capsetId,uint32_t guestCapsSize,uint8_t * capset)1101 bool EmulatedVirtioGpu::GetCaps(uint32_t capsetId, uint32_t guestCapsSize, uint8_t* capset) {
1102 return mImpl->GetCaps(capsetId, guestCapsSize, capset);
1103 }
1104
Map(uint32_t resourceId)1105 uint8_t* EmulatedVirtioGpu::Map(uint32_t resourceId) { return mImpl->Map(resourceId); }
1106
Unmap(uint32_t resourceId)1107 void EmulatedVirtioGpu::Unmap(uint32_t resourceId) { mImpl->Unmap(resourceId); }
1108
SubmitCmd(uint32_t contextId,uint32_t cmdSize,void * cmd,uint32_t ringIdx,VirtioGpuFenceFlags fenceFlags,uint32_t & fenceId,std::optional<uint32_t> blobResourceId)1109 int EmulatedVirtioGpu::SubmitCmd(uint32_t contextId, uint32_t cmdSize, void* cmd, uint32_t ringIdx,
1110 VirtioGpuFenceFlags fenceFlags, uint32_t& fenceId,
1111 std::optional<uint32_t> blobResourceId) {
1112 return mImpl->SubmitCmd(contextId, cmdSize, cmd, ringIdx, fenceFlags, fenceId, blobResourceId);
1113 }
1114
Wait(uint32_t resourceId)1115 int EmulatedVirtioGpu::Wait(uint32_t resourceId) { return mImpl->Wait(resourceId); }
1116
TransferFromHost(uint32_t contextId,uint32_t resourceId,uint32_t offset,uint32_t size)1117 int EmulatedVirtioGpu::TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t offset,
1118 uint32_t size) {
1119 return mImpl->TransferFromHost(contextId, resourceId, offset, size);
1120 }
1121
TransferFromHost(uint32_t contextId,uint32_t resourceId,uint32_t x,uint32_t y,uint32_t w,uint32_t h)1122 int EmulatedVirtioGpu::TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t x,
1123 uint32_t y, uint32_t w, uint32_t h) {
1124 return mImpl->TransferFromHost(contextId, resourceId, x, y, w, h);
1125 }
1126
TransferToHost(uint32_t contextId,uint32_t resourceId,uint32_t offset,uint32_t size)1127 int EmulatedVirtioGpu::TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t offset,
1128 uint32_t size) {
1129 return mImpl->TransferToHost(contextId, resourceId, offset, size);
1130 }
1131
TransferToHost(uint32_t contextId,uint32_t resourceId,uint32_t x,uint32_t y,uint32_t w,uint32_t h)1132 int EmulatedVirtioGpu::TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t x,
1133 uint32_t y, uint32_t w, uint32_t h) {
1134 return mImpl->TransferToHost(contextId, resourceId, x, y, w, h);
1135 }
1136
CreateBlob(uint32_t contextId,uint32_t blobMem,uint32_t blobFlags,uint64_t blobId,uint64_t blobSize)1137 std::optional<uint32_t> EmulatedVirtioGpu::CreateBlob(uint32_t contextId, uint32_t blobMem,
1138 uint32_t blobFlags, uint64_t blobId,
1139 uint64_t blobSize) {
1140 return mImpl->CreateBlob(contextId, blobMem, blobFlags, blobId, blobSize);
1141 }
1142
CreateVirglBlob(uint32_t contextId,uint32_t width,uint32_t height,uint32_t virglFormat,uint32_t target,uint32_t bind,uint32_t size)1143 std::optional<uint32_t> EmulatedVirtioGpu::CreateVirglBlob(uint32_t contextId, uint32_t width,
1144 uint32_t height, uint32_t virglFormat,
1145 uint32_t target, uint32_t bind,
1146 uint32_t size) {
1147 return mImpl->CreateVirglBlob(contextId, width, height, virglFormat, target, bind, size);
1148 }
1149
DestroyResource(uint32_t contextId,uint32_t resourceId)1150 void EmulatedVirtioGpu::DestroyResource(uint32_t contextId, uint32_t resourceId) {
1151 mImpl->DestroyResource(contextId, resourceId);
1152 }
1153
SnapshotSave(const std::string & directory)1154 void EmulatedVirtioGpu::SnapshotSave(const std::string& directory) {
1155 mImpl->SnapshotSave(directory);
1156 }
1157
SnapshotRestore(const std::string & directory)1158 void EmulatedVirtioGpu::SnapshotRestore(const std::string& directory) {
1159 mImpl->SnapshotRestore(directory);
1160 }
1161
WaitOnEmulatedFence(int fenceAsFileDescriptor,int timeoutMilliseconds)1162 int EmulatedVirtioGpu::WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds) {
1163 return mImpl->WaitOnEmulatedFence(fenceAsFileDescriptor, timeoutMilliseconds);
1164 }
1165
SignalEmulatedFence(int fenceId)1166 void EmulatedVirtioGpu::SignalEmulatedFence(int fenceId) { mImpl->SignalEmulatedFence(fenceId); }
1167
GetNumActiveEmulatedVirtioGpuUsers()1168 bool GetNumActiveEmulatedVirtioGpuUsers() { return EmulatedVirtioGpu::GetNumActiveUsers(); }
1169
1170 } // namespace gfxstream
1171