• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 #include <cstdlib>
23 #include <future>
24 #include <memory>
25 #include <optional>
26 #include <queue>
27 #include <sstream>
28 #include <string>
29 #include <thread>
30 #include <unordered_map>
31 #include <variant>
32 
33 #include "gfxstream/virtio-gpu-gfxstream-renderer.h"
34 #include "gfxstream/virtio-gpu-gfxstream-renderer-unstable.h"
35 
36 namespace gfxstream {
37 namespace {
38 
39 constexpr const uint32_t kInvalidContextId = 0;
40 
Split(const std::string & s,const std::string & delimiters)41 std::vector<std::string> Split(const std::string& s, const std::string& delimiters) {
42     if (delimiters.empty()) {
43         return {};
44     }
45 
46     std::vector<std::string> result;
47 
48     size_t base = 0;
49     size_t found;
50     while (true) {
51         found = s.find_first_of(delimiters, base);
52         result.push_back(s.substr(base, found - base));
53         if (found == s.npos) break;
54         base = found + 1;
55     }
56 
57     return result;
58 }
59 
Join(const std::vector<std::string> & things,const std::string & separator)60 std::string Join(const std::vector<std::string>& things, const std::string& separator) {
61     if (things.empty()) {
62         return "";
63     }
64 
65     std::ostringstream result;
66     result << *things.begin();
67     for (auto it = std::next(things.begin()); it != things.end(); ++it) {
68         result << separator << *it;
69     }
70     return result.str();
71 }
72 
73 }  // namespace
74 
75 class EmulatedVirtioGpu::EmulatedVirtioGpuImpl {
76    public:
77     EmulatedVirtioGpuImpl();
78     ~EmulatedVirtioGpuImpl();
79 
80     bool Init(bool withGl, bool withVk, bool withVkSnapshots, EmulatedVirtioGpu* parent);
81 
82     VirtGpuCaps GetCaps(VirtGpuCapset capset);
83 
84     std::optional<uint32_t> CreateContext();
85     void DestroyContext(uint32_t contextId);
86 
87     uint8_t* Map(uint32_t resourceId);
88     void Unmap(uint32_t resourceId);
89 
90     int ExecBuffer(uint32_t contextId, struct VirtGpuExecBuffer& execbuffer,
91                    std::optional<uint32_t> blobResourceId);
92 
93     int Wait(uint32_t resourceId);
94 
95     int TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
96                          uint32_t transferSize);
97     int TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
98                        uint32_t transferSize);
99 
100     std::optional<uint32_t> CreateBlob(uint32_t contextId, const struct VirtGpuCreateBlob& params);
101     std::optional<uint32_t> CreateVirglBlob(uint32_t contextId, uint32_t width, uint32_t height,
102                                             uint32_t virglFormat);
103 
104     void DestroyResource(uint32_t contextId, uint32_t resourceId);
105 
106     uint32_t CreateEmulatedFence();
107 
108     void SignalEmulatedFence(uint32_t fenceId);
109 
110     int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds);
111 
112    private:
113     struct VirtioGpuTaskContextAttachResource {
114         uint32_t contextId;
115         uint32_t resourceId;
116     };
117     struct VirtioGpuTaskContextDetachResource {
118         uint32_t contextId;
119         uint32_t resourceId;
120     };
121     struct VirtioGpuTaskCreateContext {
122         uint32_t contextId;
123         uint32_t contextInit;
124         std::string contextName;
125     };
126     struct VirtioGpuTaskCreateBlob {
127         uint32_t contextId;
128         uint32_t resourceId;
129         struct stream_renderer_create_blob params;
130     };
131     struct VirtioGpuTaskCreateResource {
132         uint32_t contextId;
133         uint32_t resourceId;
134         uint8_t* resourceBytes;
135         struct stream_renderer_resource_create_args params;
136     };
137     struct VirtioGpuTaskDestroyContext {
138         uint32_t contextId;
139     };
140     struct VirtioGpuTaskMap {
141         uint32_t resourceId;
142         std::promise<uint8_t*> resourceMappedPromise;
143     };
144     struct VirtioGpuTaskExecBuffer {
145         uint32_t contextId;
146         std::vector<std::byte> commandBuffer;
147     };
148     struct VirtioGpuTaskTransferToHost {
149         uint32_t contextId;
150         uint32_t resourceId;
151         uint32_t transferOffset;
152         uint32_t transferSize;
153     };
154     struct VirtioGpuTaskTransferFromHost {
155         uint32_t contextId;
156         uint32_t resourceId;
157         uint32_t transferOffset;
158         uint32_t transferSize;
159     };
160     struct VirtioGpuTaskUnrefResource {
161         uint32_t resourceId;
162     };
163     using VirtioGpuTask =
164         std::variant<VirtioGpuTaskContextAttachResource, VirtioGpuTaskContextDetachResource,
165                      VirtioGpuTaskCreateBlob, VirtioGpuTaskCreateContext,
166                      VirtioGpuTaskCreateResource, VirtioGpuTaskDestroyContext, VirtioGpuTaskMap,
167                      VirtioGpuTaskExecBuffer, VirtioGpuTaskTransferFromHost,
168                      VirtioGpuTaskTransferToHost, VirtioGpuTaskUnrefResource>;
169     struct VirtioGpuTaskWithWaitable {
170         uint32_t contextId;
171         VirtioGpuTask task;
172         std::promise<void> taskCompletedSignaler;
173         std::optional<uint32_t> fence;
174     };
175 
176     std::shared_future<void> EnqueueVirtioGpuTask(uint32_t contextId, VirtioGpuTask task,
177                                                   std::optional<uint32_t> fence = std::nullopt);
178     void DoTask(VirtioGpuTaskContextAttachResource task);
179     void DoTask(VirtioGpuTaskContextDetachResource task);
180     void DoTask(VirtioGpuTaskCreateContext task);
181     void DoTask(VirtioGpuTaskCreateBlob task);
182     void DoTask(VirtioGpuTaskCreateResource task);
183     void DoTask(VirtioGpuTaskDestroyContext task);
184     void DoTask(VirtioGpuTaskMap task);
185     void DoTask(VirtioGpuTaskExecBuffer task);
186     void DoTask(VirtioGpuTaskTransferFromHost task);
187     void DoTask(VirtioGpuTaskTransferToHost task);
188     void DoTask(VirtioGpuTaskWithWaitable task);
189     void DoTask(VirtioGpuTaskUnrefResource task);
190 
191     void RunVirtioGpuTaskProcessingLoop();
192 
193     std::atomic<uint32_t> mNextContextId{1};
194     std::atomic<uint32_t> mNextVirtioGpuResourceId{1};
195     std::atomic<uint32_t> mNextVirtioGpuFenceId{1};
196 
197     std::atomic_bool mShuttingDown{false};
198 
199     std::mutex mTasksMutex;
200     std::queue<VirtioGpuTaskWithWaitable> mTasks;
201 
202     enum class EmulatedResourceType {
203         kBlob,
204         kPipe,
205     };
206     struct EmulatedResource {
207         EmulatedResourceType type;
208 
209         std::mutex pendingWaitablesMutex;
210         std::vector<std::shared_future<void>> pendingWaitables;
211 
212         // For non-blob resources, the guest shadow memory.
213         std::unique_ptr<uint8_t[]> guestBytes;
214 
215         // For mappable blob resources, the host memory once it is mapped.
216         std::shared_future<uint8_t*> mappedHostBytes;
217     };
218     std::mutex mResourcesMutex;
219     std::unordered_map<uint32_t, EmulatedResource> mResources;
220 
CreateResource(uint32_t resourceId,EmulatedResourceType resourceType)221     EmulatedResource* CreateResource(uint32_t resourceId, EmulatedResourceType resourceType) {
222         std::lock_guard<std::mutex> lock(mResourcesMutex);
223 
224         auto [it, created] = mResources.emplace(
225             std::piecewise_construct, std::forward_as_tuple(resourceId), std::forward_as_tuple());
226         if (!created) {
227             ALOGE("Created resource %" PRIu32 " twice?", resourceId);
228         }
229 
230         EmulatedResource* resource = &it->second;
231         resource->type = resourceType;
232         return resource;
233     }
234 
GetResource(uint32_t resourceId)235     EmulatedResource* GetResource(uint32_t resourceId) {
236         std::lock_guard<std::mutex> lock(mResourcesMutex);
237 
238         auto it = mResources.find(resourceId);
239         if (it == mResources.end()) {
240             return nullptr;
241         }
242 
243         return &it->second;
244     }
245 
DeleteResource(uint32_t resourceId)246     void DeleteResource(uint32_t resourceId) {
247         std::lock_guard<std::mutex> lock(mResourcesMutex);
248         mResources.erase(resourceId);
249     }
250 
251     struct EmulatedFence {
252         std::promise<void> signaler;
253         std::shared_future<void> waitable;
254     };
255     std::mutex mVirtioGpuFencesMutex;
256     std::unordered_map<uint32_t, EmulatedFence> mVirtioGpuFences;
257 
258     std::thread mWorkerThread;
259 };
260 
EmulatedVirtioGpuImpl()261 EmulatedVirtioGpu::EmulatedVirtioGpuImpl::EmulatedVirtioGpuImpl()
262     : mWorkerThread([this]() { RunVirtioGpuTaskProcessingLoop(); }) {}
263 
~EmulatedVirtioGpuImpl()264 EmulatedVirtioGpu::EmulatedVirtioGpuImpl::~EmulatedVirtioGpuImpl() {
265     mShuttingDown = true;
266     mWorkerThread.join();
267 
268     stream_renderer_teardown();
269 }
270 
271 namespace {
272 
WriteFenceTrampoline(void * cookie,struct stream_renderer_fence * fence)273 void WriteFenceTrampoline(void* cookie, struct stream_renderer_fence* fence) {
274     auto* gpu = reinterpret_cast<EmulatedVirtioGpu*>(cookie);
275     gpu->SignalEmulatedFence(fence->fence_id);
276 }
277 
278 }  // namespace
279 
Init(bool withGl,bool withVk,bool withVkSnapshots,EmulatedVirtioGpu * parent)280 bool EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Init(bool withGl, bool withVk, bool withVkSnapshots,
281                                                     EmulatedVirtioGpu* parent) {
282     std::vector<stream_renderer_param> renderer_params{
283         stream_renderer_param{
284             .key = STREAM_RENDERER_PARAM_USER_DATA,
285             .value = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(parent)),
286         },
287         stream_renderer_param{
288             .key = STREAM_RENDERER_PARAM_FENCE_CALLBACK,
289             .value = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&WriteFenceTrampoline)),
290         },
291         stream_renderer_param{
292             .key = STREAM_RENDERER_PARAM_RENDERER_FLAGS,
293             .value =
294                 static_cast<uint64_t>(STREAM_RENDERER_FLAGS_USE_SURFACELESS_BIT) |
295                 (withGl ? static_cast<uint64_t>(STREAM_RENDERER_FLAGS_USE_EGL_BIT |
296                                                 STREAM_RENDERER_FLAGS_USE_GLES_BIT)
297                         : 0) |
298                 (withVk ? static_cast<uint64_t>(STREAM_RENDERER_FLAGS_USE_VK_BIT) : 0) |
299                 (withVkSnapshots ? static_cast<uint64_t>(STREAM_RENDERER_FLAGS_VULKAN_SNAPSHOTS)
300                                  : 0),
301         },
302         stream_renderer_param{
303             .key = STREAM_RENDERER_PARAM_WIN0_WIDTH,
304             .value = 32,
305         },
306         stream_renderer_param{
307             .key = STREAM_RENDERER_PARAM_WIN0_HEIGHT,
308             .value = 32,
309         },
310     };
311     return stream_renderer_init(renderer_params.data(), renderer_params.size()) == 0;
312 }
313 
GetCaps(VirtGpuCapset capset)314 VirtGpuCaps EmulatedVirtioGpu::EmulatedVirtioGpuImpl::GetCaps(VirtGpuCapset capset) {
315     VirtGpuCaps caps = {
316         .params =
317             {
318                 [kParam3D] = 1,
319                 [kParamCapsetFix] = 1,
320                 [kParamResourceBlob] = 1,
321                 [kParamHostVisible] = 1,
322                 [kParamCrossDevice] = 0,
323                 [kParamContextInit] = 1,
324                 [kParamSupportedCapsetIds] = 0,
325                 [kParamCreateGuestHandle] = 0,
326             },
327     };
328 
329     stream_renderer_fill_caps(static_cast<uint32_t>(capset), 0, &caps.vulkanCapset);
330 
331     return caps;
332 }
333 
CreateContext()334 std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateContext() {
335     const uint32_t contextId = mNextContextId++;
336 
337     VirtioGpuTaskCreateContext task = {
338         .contextId = contextId,
339         .contextInit = 0,
340         .contextName = "EmulatedVirtioGpu Context " + std::to_string(contextId),
341     };
342     EnqueueVirtioGpuTask(contextId, std::move(task));
343     return contextId;
344 }
345 
DestroyContext(uint32_t contextId)346 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DestroyContext(uint32_t contextId) {
347     VirtioGpuTaskDestroyContext task = {
348         .contextId = contextId,
349     };
350     EnqueueVirtioGpuTask(contextId, std::move(task));
351 }
352 
Map(uint32_t resourceId)353 uint8_t* EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Map(uint32_t resourceId) {
354     EmulatedResource* resource = GetResource(resourceId);
355     if (resource == nullptr) {
356         ALOGE("Failed to Map() resource %" PRIu32 ": not found.", resourceId);
357         return nullptr;
358     }
359 
360     uint8_t* mapped = nullptr;
361     if (resource->type == EmulatedResourceType::kBlob) {
362         if (!resource->mappedHostBytes.valid()) {
363             ALOGE("Failed to Map() resource %" PRIu32
364                   ": attempting to map blob "
365                   "without mappable flag?",
366                   resourceId);
367             return nullptr;
368         }
369         mapped = resource->mappedHostBytes.get();
370     } else if (resource->type == EmulatedResourceType::kPipe) {
371         mapped = resource->guestBytes.get();
372     }
373     return mapped;
374 }
375 
Unmap(uint32_t resourceId)376 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Unmap(uint32_t resourceId) {
377     stream_renderer_resource_unmap(resourceId);
378 }
379 
Wait(uint32_t resourceId)380 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Wait(uint32_t resourceId) {
381     EmulatedResource* resource = GetResource(resourceId);
382     if (resource == nullptr) {
383         ALOGE("Failed to Wait() on resource %" PRIu32 ": not found.", resourceId);
384         return -1;
385     }
386 
387     std::vector<std::shared_future<void>> pendingWaitables;
388     {
389         std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
390         pendingWaitables = resource->pendingWaitables;
391         resource->pendingWaitables.clear();
392     }
393     for (auto& waitable : pendingWaitables) {
394         waitable.wait();
395     }
396 
397     return 0;
398 }
399 
TransferFromHost(uint32_t contextId,uint32_t resourceId,uint32_t transferOffset,uint32_t transferSize)400 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferFromHost(uint32_t contextId,
401                                                                uint32_t resourceId,
402                                                                uint32_t transferOffset,
403                                                                uint32_t transferSize) {
404     EmulatedResource* resource = GetResource(resourceId);
405     if (resource == nullptr) {
406         ALOGE("Failed to TransferFromHost() on resource %" PRIu32 ": not found.", resourceId);
407         return -1;
408     }
409 
410     VirtioGpuTaskTransferFromHost task = {
411         .contextId = contextId,
412         .resourceId = resourceId,
413         .transferOffset = transferOffset,
414         .transferSize = transferSize,
415     };
416     auto waitable = EnqueueVirtioGpuTask(contextId, std::move(task));
417 
418     {
419         std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
420         resource->pendingWaitables.push_back(std::move(waitable));
421     }
422 
423     return 0;
424 }
425 
TransferToHost(uint32_t contextId,uint32_t resourceId,uint32_t transferOffset,uint32_t transferSize)426 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferToHost(uint32_t contextId,
427                                                              uint32_t resourceId,
428                                                              uint32_t transferOffset,
429                                                              uint32_t transferSize) {
430     EmulatedResource* resource = GetResource(resourceId);
431     if (resource == nullptr) {
432         ALOGE("Failed to TransferFromHost() on resource %" PRIu32 ": not found.", resourceId);
433         return -1;
434     }
435 
436     VirtioGpuTaskTransferToHost task = {
437         .contextId = contextId,
438         .resourceId = resourceId,
439         .transferOffset = transferOffset,
440         .transferSize = transferSize,
441     };
442     auto waitable = EnqueueVirtioGpuTask(contextId, std::move(task));
443 
444     {
445         std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
446         resource->pendingWaitables.push_back(std::move(waitable));
447     }
448 
449     return 0;
450 }
451 
CreateBlob(uint32_t contextId,const struct VirtGpuCreateBlob & blobCreate)452 std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateBlob(
453     uint32_t contextId, const struct VirtGpuCreateBlob& blobCreate) {
454 
455     const uint32_t resourceId = mNextVirtioGpuResourceId++;
456 
457     ALOGV("Enquing task to create blob resource-id:%d size:%" PRIu64, resourceId, blobCreate.size);
458 
459     EmulatedResource* resource = CreateResource(resourceId, EmulatedResourceType::kBlob);
460 
461     VirtioGpuTaskCreateBlob createTask{
462         .contextId = contextId,
463         .resourceId = resourceId,
464         .params =
465             {
466                 .blob_mem = static_cast<uint32_t>(blobCreate.blobMem),
467                 .blob_flags = static_cast<uint32_t>(blobCreate.flags),
468                 .blob_id = blobCreate.blobId,
469                 .size = blobCreate.size,
470             },
471     };
472     auto createBlobCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(createTask));
473     resource->pendingWaitables.push_back(std::move(createBlobCompletedWaitable));
474 
475     if (blobCreate.flags & kBlobFlagMappable) {
476         std::promise<uint8_t*> mappedBytesPromise;
477         std::shared_future<uint8_t*> mappedBytesWaitable = mappedBytesPromise.get_future();
478 
479         VirtioGpuTaskMap mapTask{
480             .resourceId = resourceId,
481             .resourceMappedPromise = std::move(mappedBytesPromise),
482         };
483         EnqueueVirtioGpuTask(contextId, std::move(mapTask));
484         resource->mappedHostBytes = std::move(mappedBytesWaitable);
485     }
486 
487     VirtioGpuTaskContextAttachResource attachTask{
488         .contextId = contextId,
489         .resourceId = resourceId,
490     };
491     EnqueueVirtioGpuTask(contextId, std::move(attachTask));
492 
493     return resourceId;
494 }
495 
CreateVirglBlob(uint32_t contextId,uint32_t width,uint32_t height,uint32_t virglFormat)496 std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateVirglBlob(
497     uint32_t contextId, uint32_t width, uint32_t height, uint32_t virglFormat) {
498 
499     const uint32_t resourceId = mNextVirtioGpuResourceId++;
500 
501     EmulatedResource* resource = CreateResource(resourceId, EmulatedResourceType::kPipe);
502 
503     uint32_t target = 0;
504     uint32_t bind = 0;
505     uint32_t bpp = 0;
506 
507     switch (virglFormat) {
508         case VIRGL_FORMAT_R8G8B8A8_UNORM:
509         case VIRGL_FORMAT_B8G8R8A8_UNORM:
510             target = PIPE_TEXTURE_2D;
511             bind = VIRGL_BIND_RENDER_TARGET;
512             bpp = 4;
513             break;
514         case VIRGL_FORMAT_B5G6R5_UNORM:
515             target = PIPE_TEXTURE_2D;
516             bind = VIRGL_BIND_RENDER_TARGET;
517             bpp = 2;
518             break;
519         case VIRGL_FORMAT_R8G8B8_UNORM:
520             target = PIPE_TEXTURE_2D;
521             bind = VIRGL_BIND_RENDER_TARGET;
522             bpp = 3;
523             break;
524         case VIRGL_FORMAT_R8_UNORM:
525             target = PIPE_BUFFER;
526             bind = VIRGL_BIND_CUSTOM;
527             bpp = 1;
528             break;
529         default:
530             ALOGE("Unknown virgl format %u", virglFormat);
531             return {};
532     }
533 
534     resource->guestBytes = std::make_unique<uint8_t[]>(width * height * bpp);
535 
536     VirtioGpuTaskCreateResource task{
537         .contextId = contextId,
538         .resourceId = resourceId,
539         .resourceBytes = resource->guestBytes.get(),
540         .params =
541             {
542                 .handle = resourceId,
543                 .target = target,
544                 .format = virglFormat,
545                 .bind = bind,
546                 .width = width,
547                 .height = height,
548                 .depth = 1,
549                 .array_size = 1,
550                 .last_level = 0,
551                 .nr_samples = 0,
552                 .flags = 0,
553             },
554     };
555     auto taskCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(task));
556     resource->pendingWaitables.push_back(std::move(taskCompletedWaitable));
557 
558     VirtioGpuTaskContextAttachResource attachTask{
559         .contextId = contextId,
560         .resourceId = resourceId,
561     };
562     EnqueueVirtioGpuTask(contextId, std::move(attachTask));
563 
564     return resourceId;
565 }
566 
DestroyResource(uint32_t contextId,uint32_t resourceId)567 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DestroyResource(uint32_t contextId,
568                                                                uint32_t resourceId) {
569     DeleteResource(resourceId);
570 
571     VirtioGpuTaskUnrefResource unrefTask{
572         .resourceId = resourceId,
573     };
574     EnqueueVirtioGpuTask(contextId, std::move(unrefTask));
575 
576     VirtioGpuTaskContextDetachResource detachTask{
577         .contextId = contextId,
578         .resourceId = resourceId,
579     };
580     EnqueueVirtioGpuTask(contextId, std::move(detachTask));
581 }
582 
ExecBuffer(uint32_t contextId,struct VirtGpuExecBuffer & execbuffer,std::optional<uint32_t> blobResourceId)583 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::ExecBuffer(uint32_t contextId,
584                                                          struct VirtGpuExecBuffer& execbuffer,
585                                                          std::optional<uint32_t> blobResourceId) {
586     std::optional<uint32_t> fence;
587 
588     if (execbuffer.flags & kFenceOut) {
589         fence = CreateEmulatedFence();
590     }
591 
592     const VirtioGpuTaskExecBuffer task = {
593         .contextId = contextId,
594         .commandBuffer = std::vector<std::byte>(
595             reinterpret_cast<std::byte*>(execbuffer.command),
596             reinterpret_cast<std::byte*>(execbuffer.command) + execbuffer.command_size),
597     };
598     auto taskCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(task), fence);
599 
600     if (blobResourceId) {
601         EmulatedResource* resource = GetResource(*blobResourceId);
602         if (resource == nullptr) {
603             ALOGE("Failed to ExecBuffer() with resource %" PRIu32 ": not found.", *blobResourceId);
604             return -1;
605         }
606 
607         {
608             std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex);
609             resource->pendingWaitables.push_back(std::move(taskCompletedWaitable));
610         }
611     }
612 
613     if (execbuffer.flags & kFenceOut) {
614         execbuffer.handle.osHandle = *fence;
615         execbuffer.handle.type = kFenceHandleSyncFd;
616     }
617 
618     return 0;
619 }
620 
WaitOnEmulatedFence(int fenceAsFileDescriptor,int timeoutMilliseconds)621 int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::WaitOnEmulatedFence(int fenceAsFileDescriptor,
622                                                                   int timeoutMilliseconds) {
623     uint32_t fenceId = static_cast<uint32_t>(fenceAsFileDescriptor);
624     ALOGV("Waiting on fence:%d", (int)fenceId);
625 
626     std::shared_future<void> waitable;
627 
628     {
629         std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex);
630 
631         auto fenceIt = mVirtioGpuFences.find(fenceId);
632         if (fenceIt == mVirtioGpuFences.end()) {
633             ALOGE("Fence:%d already signaled", (int)fenceId);
634             return 0;
635         }
636         auto& fence = fenceIt->second;
637 
638         waitable = fence.waitable;
639     }
640 
641     auto status = waitable.wait_for(std::chrono::milliseconds(timeoutMilliseconds));
642     if (status == std::future_status::ready) {
643         ALOGV("Finished waiting for fence:%d", (int)fenceId);
644         return 0;
645     } else {
646         ALOGE("Timed out waiting for fence:%d", (int)fenceId);
647         return -1;
648     }
649 }
650 
SignalEmulatedFence(uint32_t fenceId)651 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::SignalEmulatedFence(uint32_t fenceId) {
652     ALOGV("Signaling fence:%d", (int)fenceId);
653 
654     std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex);
655 
656     auto fenceIt = mVirtioGpuFences.find(fenceId);
657     if (fenceIt == mVirtioGpuFences.end()) {
658         ALOGE("Failed to find fence %" PRIu32, fenceId);
659         return;
660     }
661     auto& fenceInfo = fenceIt->second;
662     fenceInfo.signaler.set_value();
663 }
664 
CreateEmulatedFence()665 uint32_t EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateEmulatedFence() {
666     const uint32_t fenceId = mNextVirtioGpuFenceId++;
667 
668     ALOGV("Creating fence:%d", (int)fenceId);
669 
670     std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex);
671 
672     auto [fenceIt, fenceCreated] = mVirtioGpuFences.emplace(fenceId, EmulatedFence{});
673     if (!fenceCreated) {
674         ALOGE("Attempting to recreate fence %" PRIu32, fenceId);
675     }
676 
677     auto& fenceInfo = fenceIt->second;
678     fenceInfo.waitable = fenceInfo.signaler.get_future();
679 
680     return fenceId;
681 }
682 
EnqueueVirtioGpuTask(uint32_t contextId,VirtioGpuTask task,std::optional<uint32_t> fence)683 std::shared_future<void> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::EnqueueVirtioGpuTask(
684     uint32_t contextId, VirtioGpuTask task, std::optional<uint32_t> fence) {
685     std::promise<void> taskCompletedSignaler;
686     std::shared_future<void> taskCompletedWaitable(taskCompletedSignaler.get_future());
687 
688     std::lock_guard<std::mutex> lock(mTasksMutex);
689     mTasks.push(VirtioGpuTaskWithWaitable{
690         .contextId = contextId,
691         .task = std::move(task),
692         .taskCompletedSignaler = std::move(taskCompletedSignaler),
693         .fence = fence,
694     });
695 
696     return taskCompletedWaitable;
697 }
698 
DoTask(VirtioGpuTaskContextAttachResource task)699 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskContextAttachResource task) {
700     ALOGV("Performing task to attach resource-id:%d to context-id:%d", task.resourceId,
701           task.contextId);
702 
703     stream_renderer_ctx_attach_resource(task.contextId, task.resourceId);
704 
705     ALOGV("Performing task to attach resource-id:%d to context-id:%d - done", task.resourceId,
706           task.contextId);
707 }
708 
DoTask(VirtioGpuTaskContextDetachResource task)709 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskContextDetachResource task) {
710     ALOGV("Performing task to detach resource-id:%d to context-id:%d", task.resourceId,
711           task.contextId);
712 
713     stream_renderer_ctx_detach_resource(task.contextId, task.resourceId);
714 
715     ALOGV("Performing task to detach resource-id:%d to context-id:%d - done", task.resourceId,
716           task.contextId);
717 }
718 
DoTask(VirtioGpuTaskCreateBlob task)719 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateBlob task) {
720     ALOGV("Performing task to create blob resource-id:%d", task.resourceId);
721 
722     int ret = stream_renderer_create_blob(task.contextId, task.resourceId, &task.params,
723                                           /*iovecs=*/nullptr,
724                                           /*num_iovs=*/0,
725                                           /*handle=*/nullptr);
726     if (ret) {
727         ALOGE("Failed to create blob.");
728     }
729 
730     ALOGV("Performing task to create blob resource-id:%d - done", task.resourceId);
731 }
732 
DoTask(VirtioGpuTaskCreateContext task)733 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateContext task) {
734     ALOGV("Performing task to create context-id:%" PRIu32 " context-init:%" PRIu32
735           " context-name:%s",
736           task.contextId, task.contextInit, task.contextName.c_str());
737 
738     int ret = stream_renderer_context_create(task.contextId, task.contextName.size(),
739                                              task.contextName.data(), task.contextInit);
740     if (ret) {
741         ALOGE("Failed to create context-id:%" PRIu32 ".", task.contextId);
742         return;
743     }
744 
745     ALOGV("Performing task to create context-id:%" PRIu32 " context-init:%" PRIu32
746           " context-name:%s - done",
747           task.contextId, task.contextInit, task.contextName.c_str());
748 }
749 
DoTask(VirtioGpuTaskCreateResource task)750 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateResource task) {
751     ALOGV("Performing task to create resource resource:%d", task.resourceId);
752 
753     int ret = stream_renderer_resource_create(&task.params, nullptr, 0);
754     if (ret) {
755         ALOGE("Failed to create resource:%d", task.resourceId);
756     }
757 
758     struct iovec iov = {
759         .iov_base = task.resourceBytes,
760         .iov_len = task.params.width,
761     };
762     ret = stream_renderer_resource_attach_iov(task.resourceId, &iov, 1);
763     if (ret) {
764         ALOGE("Failed to attach iov to resource:%d", task.resourceId);
765     }
766 
767     ALOGV("Performing task to create resource resource:%d - done", task.resourceId);
768 
769     stream_renderer_ctx_attach_resource(task.contextId, task.resourceId);
770 }
771 
DoTask(VirtioGpuTaskDestroyContext task)772 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskDestroyContext task) {
773     ALOGV("Performing task to destroy context-id:%" PRIu32, task.contextId);
774 
775     stream_renderer_context_destroy(task.contextId);
776 
777     ALOGV("Performing task to destroy context-id:%" PRIu32 " - done", task.contextId);
778 }
779 
DoTask(VirtioGpuTaskMap task)780 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskMap task) {
781     ALOGV("Performing task to map resource resource:%d", task.resourceId);
782 
783     void* mapped = nullptr;
784 
785     int ret = stream_renderer_resource_map(task.resourceId, &mapped, nullptr);
786     if (ret) {
787         ALOGE("Failed to map resource:%d", task.resourceId);
788         return;
789     }
790 
791     task.resourceMappedPromise.set_value(reinterpret_cast<uint8_t*>(mapped));
792     ALOGV("Performing task to map resource resource:%d - done", task.resourceId);
793 }
794 
DoTask(VirtioGpuTaskExecBuffer task)795 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskExecBuffer task) {
796     ALOGV("Performing task to execbuffer");
797 
798     if (task.commandBuffer.size() % 4 != 0) {
799         ALOGE("Unaligned command buffer?");
800         return;
801     }
802 
803     stream_renderer_command cmd = {
804         .ctx_id = task.contextId,
805         .cmd_size = static_cast<uint32_t>(task.commandBuffer.size()),
806         .cmd = reinterpret_cast<uint8_t*>(task.commandBuffer.data()),
807         .num_in_fences = 0,
808         .fences = nullptr,
809     };
810 
811     int ret = stream_renderer_submit_cmd(&cmd);
812     if (ret) {
813         ALOGE("Failed to execbuffer.");
814     }
815 
816     ALOGV("Performing task to execbuffer - done");
817 }
818 
DoTask(VirtioGpuTaskTransferFromHost task)819 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskTransferFromHost task) {
820     struct stream_renderer_box transferBox = {
821         .x = task.transferOffset,
822         .y = 0,
823         .z = 0,
824         .w = task.transferSize,
825         .h = 1,
826         .d = 1,
827     };
828 
829     int ret = stream_renderer_transfer_read_iov(task.resourceId, task.contextId,
830                                                 /*level=*/0,
831                                                 /*stride=*/0,
832                                                 /*layer_stride=*/0, &transferBox,
833                                                 /*offset=*/0,
834                                                 /*iov=*/nullptr,
835                                                 /*iovec_cnt=*/0);
836     if (ret) {
837         ALOGE("Failed to transferFromHost() for resource:%" PRIu32, task.resourceId);
838     }
839 }
840 
DoTask(VirtioGpuTaskTransferToHost task)841 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskTransferToHost task) {
842     struct stream_renderer_box transferBox = {
843         .x = task.transferOffset,
844         .y = 0,
845         .z = 0,
846         .w = task.transferSize,
847         .h = 1,
848         .d = 1,
849     };
850 
851     int ret = stream_renderer_transfer_write_iov(task.resourceId, task.contextId,
852                                                  /*level=*/0,
853                                                  /*stride=*/0,
854                                                  /*layer_stride=*/0, &transferBox,
855                                                  /*offset=*/0,
856                                                  /*iov=*/nullptr,
857                                                  /*iovec_cnt=*/0);
858     if (ret) {
859         ALOGE("Failed to transferToHost() for resource:%" PRIu32, task.resourceId);
860     }
861 }
862 
DoTask(VirtioGpuTaskUnrefResource task)863 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskUnrefResource task) {
864     stream_renderer_resource_unref(task.resourceId);
865 }
866 
DoTask(VirtioGpuTaskWithWaitable task)867 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskWithWaitable task) {
868     std::visit(
869         [this](auto&& work) {
870             using T = std::decay_t<decltype(work)>;
871             if constexpr (std::is_same_v<T, VirtioGpuTaskContextAttachResource>) {
872                 DoTask(std::move(work));
873             } else if constexpr (std::is_same_v<T, VirtioGpuTaskContextDetachResource>) {
874                 DoTask(std::move(work));
875             } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateBlob>) {
876                 DoTask(std::move(work));
877             } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateContext>) {
878                 DoTask(std::move(work));
879             } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateResource>) {
880                 DoTask(std::move(work));
881             } else if constexpr (std::is_same_v<T, VirtioGpuTaskDestroyContext>) {
882                 DoTask(std::move(work));
883             } else if constexpr (std::is_same_v<T, VirtioGpuTaskMap>) {
884                 DoTask(std::move(work));
885             } else if constexpr (std::is_same_v<T, VirtioGpuTaskExecBuffer>) {
886                 DoTask(std::move(work));
887             } else if constexpr (std::is_same_v<T, VirtioGpuTaskTransferFromHost>) {
888                 DoTask(std::move(work));
889             } else if constexpr (std::is_same_v<T, VirtioGpuTaskTransferToHost>) {
890                 DoTask(std::move(work));
891             } else if constexpr (std::is_same_v<T, VirtioGpuTaskUnrefResource>) {
892                 DoTask(std::move(work));
893             }
894         },
895         task.task);
896 
897     if (task.fence) {
898         const stream_renderer_fence fenceInfo = {
899             .flags = STREAM_RENDERER_FLAG_FENCE_RING_IDX,
900             .fence_id = *task.fence,
901             .ctx_id = task.contextId,
902             .ring_idx = 0,
903         };
904         int ret = stream_renderer_create_fence(&fenceInfo);
905         if (ret) {
906             ALOGE("Failed to create fence.");
907         }
908     }
909 
910     task.taskCompletedSignaler.set_value();
911 }
912 
RunVirtioGpuTaskProcessingLoop()913 void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::RunVirtioGpuTaskProcessingLoop() {
914     while (!mShuttingDown.load()) {
915         std::optional<VirtioGpuTaskWithWaitable> task;
916 
917         {
918             std::lock_guard<std::mutex> lock(mTasksMutex);
919             if (!mTasks.empty()) {
920                 task = std::move(mTasks.front());
921                 mTasks.pop();
922             }
923         }
924 
925         if (task) {
926             DoTask(std::move(*task));
927         }
928     }
929 }
930 
931 namespace {
932 
933 EmulatedVirtioGpu* sInstance = nullptr;
934 
935 }  // namespace
936 
EmulatedVirtioGpu()937 EmulatedVirtioGpu::EmulatedVirtioGpu()
938     : mImpl{std::make_unique<EmulatedVirtioGpu::EmulatedVirtioGpuImpl>()} {}
939 
940 /*static*/
Get()941 EmulatedVirtioGpu& EmulatedVirtioGpu::Get() {
942     if (sInstance == nullptr) {
943         sInstance = new EmulatedVirtioGpu();
944 
945         bool withGl = false;
946         bool withVk = true;
947         bool withVkSnapshots = false;
948 
949         struct Option {
950             std::string env;
951             bool* val;
952         };
953         const std::vector<Option> options = {
954             {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl},
955             {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk},
956             {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK_SNAPSHOTS", &withVkSnapshots},
957         };
958         for (const Option option : options) {
959             const char* val = std::getenv(option.env.c_str());
960             if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) {
961                 *option.val = true;
962             }
963         }
964 
965         ALOGE("Initializing withGl:%d withVk:%d withVkSnapshots:%d", withGl, withVk,
966               withVkSnapshots);
967         if (!sInstance->Init(withGl, withVk, withVkSnapshots)) {
968             ALOGE("Failed to initialize EmulatedVirtioGpu.");
969         }
970     }
971     return *sInstance;
972 }
973 
974 /*static*/
Reset()975 void EmulatedVirtioGpu::Reset() {
976     if (sInstance != nullptr) {
977         delete sInstance;
978         sInstance = nullptr;
979     }
980 }
981 
Init(bool withGl,bool withVk,bool withVkSnapshots)982 bool EmulatedVirtioGpu::Init(bool withGl, bool withVk, bool withVkSnapshots) {
983     return mImpl->Init(withGl, withVk, withVkSnapshots, this);
984 }
985 
CreateContext()986 std::optional<uint32_t> EmulatedVirtioGpu::CreateContext() { return mImpl->CreateContext(); }
987 
DestroyContext(uint32_t contextId)988 void EmulatedVirtioGpu::DestroyContext(uint32_t contextId) { mImpl->DestroyContext(contextId); }
989 
GetCaps(VirtGpuCapset capset)990 VirtGpuCaps EmulatedVirtioGpu::GetCaps(VirtGpuCapset capset) { return mImpl->GetCaps(capset); }
991 
Map(uint32_t resourceId)992 uint8_t* EmulatedVirtioGpu::Map(uint32_t resourceId) { return mImpl->Map(resourceId); }
993 
Unmap(uint32_t resourceId)994 void EmulatedVirtioGpu::Unmap(uint32_t resourceId) { mImpl->Unmap(resourceId); }
995 
ExecBuffer(uint32_t contextId,struct VirtGpuExecBuffer & execbuffer,std::optional<uint32_t> blobResourceId)996 int EmulatedVirtioGpu::ExecBuffer(uint32_t contextId, struct VirtGpuExecBuffer& execbuffer,
997                                   std::optional<uint32_t> blobResourceId) {
998     return mImpl->ExecBuffer(contextId, execbuffer, blobResourceId);
999 }
1000 
Wait(uint32_t resourceId)1001 int EmulatedVirtioGpu::Wait(uint32_t resourceId) { return mImpl->Wait(resourceId); }
1002 
TransferFromHost(uint32_t contextId,uint32_t resourceId,uint32_t offset,uint32_t size)1003 int EmulatedVirtioGpu::TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t offset,
1004                                         uint32_t size) {
1005     return mImpl->TransferFromHost(contextId, resourceId, offset, size);
1006 }
1007 
TransferToHost(uint32_t contextId,uint32_t resourceId,uint32_t offset,uint32_t size)1008 int EmulatedVirtioGpu::TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t offset,
1009                                       uint32_t size) {
1010     return mImpl->TransferToHost(contextId, resourceId, offset, size);
1011 }
1012 
CreateBlob(uint32_t contextId,const struct VirtGpuCreateBlob & params)1013 std::optional<uint32_t> EmulatedVirtioGpu::CreateBlob(uint32_t contextId,
1014                                                       const struct VirtGpuCreateBlob& params) {
1015     return mImpl->CreateBlob(contextId, params);
1016 }
1017 
CreateVirglBlob(uint32_t contextId,uint32_t width,uint32_t height,uint32_t virglFormat)1018 std::optional<uint32_t> EmulatedVirtioGpu::CreateVirglBlob(uint32_t contextId, uint32_t width,
1019                                                            uint32_t height, uint32_t virglFormat) {
1020     return mImpl->CreateVirglBlob(contextId, width, height, virglFormat);
1021 }
1022 
DestroyResource(uint32_t contextId,uint32_t resourceId)1023 void EmulatedVirtioGpu::DestroyResource(uint32_t contextId, uint32_t resourceId) {
1024     mImpl->DestroyResource(contextId, resourceId);
1025 }
1026 
WaitOnEmulatedFence(int fenceAsFileDescriptor,int timeoutMilliseconds)1027 int EmulatedVirtioGpu::WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds) {
1028     return mImpl->WaitOnEmulatedFence(fenceAsFileDescriptor, timeoutMilliseconds);
1029 }
1030 
SignalEmulatedFence(int fenceId)1031 void EmulatedVirtioGpu::SignalEmulatedFence(int fenceId) { mImpl->SignalEmulatedFence(fenceId); }
1032 
ResetEmulatedVirtioGpu()1033 void ResetEmulatedVirtioGpu() { EmulatedVirtioGpu::Reset(); }
1034 
1035 }  // namespace gfxstream
1036