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 "VirtioGpuContext.h"
16 
17 #include "host-common/AddressSpaceService.h"
18 #include "host-common/opengles.h"
19 
20 namespace gfxstream {
21 namespace host {
22 
23 /*static*/
Create(const GoldfishPipeServiceOps * ops,VirtioGpuContextId contextId,const std::string & contextName,uint32_t capsetId)24 std::optional<VirtioGpuContext> VirtioGpuContext::Create(const GoldfishPipeServiceOps* ops,
25                                                          VirtioGpuContextId contextId,
26                                                          const std::string& contextName,
27                                                          uint32_t capsetId) {
28     VirtioGpuContext context = {};
29     context.mId = contextId;
30     context.mName = contextName;
31     context.mCapsetId = capsetId;
32 
33     auto hostPipe = ops->guest_open_with_flags(reinterpret_cast<GoldfishHwPipe*>(contextId),
34                                                0x1 /* is virtio */);
35     if (!hostPipe) {
36         stream_renderer_error("failed to create context %u: failed to create pipe.", contextId);
37         return std::nullopt;
38     }
39     stream_renderer_debug("created initial pipe for context %u: %p", contextId, hostPipe);
40     context.mHostPipe = hostPipe;
41 
42     android_onGuestGraphicsProcessCreate(contextId);
43 
44     return context;
45 }
46 
Destroy(const GoldfishPipeServiceOps * pipeOps,const struct address_space_device_control_ops * asgOps)47 int VirtioGpuContext::Destroy(const GoldfishPipeServiceOps* pipeOps,
48                               const struct address_space_device_control_ops* asgOps) {
49     for (const auto& [_, handle] : mAddressSpaceHandles) {
50         // Note: this can hang as is but this has only been observed to
51         // happen during shutdown. See b/329287602#comment8.
52         asgOps->destroy_handle(handle);
53     }
54 
55     if (!mHostPipe) {
56         stream_renderer_error("failed to destroy context %u: missing pipe?", mId);
57         return -EINVAL;
58     }
59     pipeOps->guest_close(mHostPipe, GOLDFISH_PIPE_CLOSE_GRACEFUL);
60 
61     android_cleanupProcGLObjects(mId);
62 
63     return 0;
64 }
65 
AttachResource(VirtioGpuResource & resource)66 void VirtioGpuContext::AttachResource(VirtioGpuResource& resource) {
67     // Associate the host pipe of the resource entry with the host pipe of
68     // the context entry.  That is, the last context to call attachResource
69     // wins if there is any conflict.
70     resource.AttachToContext(mId);
71     resource.SetHostPipe(mHostPipe);
72 
73     mAttachedResources.insert(resource.GetId());
74 }
75 
DetachResource(VirtioGpuResource & resource)76 void VirtioGpuContext::DetachResource(VirtioGpuResource& resource) {
77     mAttachedResources.erase(resource.GetId());
78     resource.DetachFromContext(mId);
79 }
80 
GetAttachedResources() const81 const std::unordered_set<VirtioGpuResourceId>& VirtioGpuContext::GetAttachedResources() const {
82     return mAttachedResources;
83 }
84 
SetHostPipe(GoldfishHostPipe * pipe)85 void VirtioGpuContext::SetHostPipe(GoldfishHostPipe* pipe) { mHostPipe = pipe; }
86 
AcquireSync(uint64_t syncId)87 int VirtioGpuContext::AcquireSync(uint64_t syncId) {
88     if (mLatestSync) {
89         stream_renderer_error(
90             "failed to acquire sync %" PRIu64 " on context %u: sync already present?", syncId, mId);
91         return -EINVAL;
92     }
93 
94     auto descriptorOpt = ExternalObjectManager::get()->removeSyncDescriptorInfo(mId, syncId);
95     if (!descriptorOpt) {
96         stream_renderer_error("failed to acquire sync %" PRIu64 " on context %u: sync not found.",
97                               syncId, mId);
98         return -EINVAL;
99     }
100 
101     mLatestSync = std::move(*descriptorOpt);
102     return 0;
103 }
104 
TakeSync()105 std::optional<SyncDescriptorInfo> VirtioGpuContext::TakeSync() {
106     if (!mLatestSync) {
107         return std::nullopt;
108     }
109 
110     auto info = std::move(*mLatestSync);
111     mLatestSync.reset();
112     return std::move(info);
113 }
114 
CreateAddressSpaceGraphicsInstance(const struct address_space_device_control_ops * asgOps,VirtioGpuResource & resource)115 int VirtioGpuContext::CreateAddressSpaceGraphicsInstance(
116     const struct address_space_device_control_ops* asgOps, VirtioGpuResource& resource) {
117     const VirtioGpuResourceId resourceId = resource.GetId();
118 
119     void* resourceHva = nullptr;
120     uint64_t resourceHvaSize = 0;
121     if (resource.Map(&resourceHva, &resourceHvaSize) != 0) {
122         stream_renderer_error(
123             "failed to create ASG instance on context %d: failed to map resource %u", mId,
124             resourceId);
125         return -EINVAL;
126     }
127 
128     const std::string asgName = mName + "-" + std::to_string(resourceId);
129 
130     // Note: resource ids can not be used as ASG handles because ASGs may outlive the
131     // containing resource due asynchronous ASG destruction.
132     const uint32_t asgId = asgOps->gen_handle();
133 
134     struct AddressSpaceCreateInfo createInfo = {
135         .handle = asgId,
136         .type = android::emulation::VirtioGpuGraphics,
137         .createRenderThread = true,
138         .externalAddr = resourceHva,
139         .externalAddrSize = resourceHvaSize,
140         .virtioGpuContextId = mId,
141         .virtioGpuCapsetId = mCapsetId,
142         .contextName = asgName.c_str(),
143         .contextNameSize = static_cast<uint32_t>(asgName.size()),
144     };
145     asgOps->create_instance(createInfo);
146 
147     mAddressSpaceHandles[resourceId] = asgId;
148     return 0;
149 }
150 
AsgInstances() const151 const std::unordered_map<VirtioGpuResourceId, uint32_t>& VirtioGpuContext::AsgInstances() const {
152     return mAddressSpaceHandles;
153 }
154 
TakeAddressSpaceGraphicsHandle(VirtioGpuResourceId resourceId)155 std::optional<uint32_t> VirtioGpuContext::TakeAddressSpaceGraphicsHandle(
156     VirtioGpuResourceId resourceId) {
157     auto asgIt = mAddressSpaceHandles.find(resourceId);
158     if (asgIt == mAddressSpaceHandles.end()) {
159         return std::nullopt;
160     }
161 
162     auto asgId = asgIt->second;
163     mAddressSpaceHandles.erase(asgIt);
164     return asgId;
165 }
166 
PingAddressSpaceGraphicsInstance(const struct address_space_device_control_ops * asgOps,VirtioGpuResourceId resourceId)167 int VirtioGpuContext::PingAddressSpaceGraphicsInstance(
168     const struct address_space_device_control_ops* asgOps, VirtioGpuResourceId resourceId) {
169     auto asgIt = mAddressSpaceHandles.find(resourceId);
170     if (asgIt == mAddressSpaceHandles.end()) {
171         stream_renderer_error(
172             "failed to ping ASG instance on context %u resource %d: ASG not found.", mId,
173             resourceId);
174         return -EINVAL;
175     }
176     auto asgId = asgIt->second;
177 
178     struct android::emulation::AddressSpaceDevicePingInfo ping = {0};
179     ping.metadata = ASG_NOTIFY_AVAILABLE;
180     asgOps->ping_at_hva(asgId, &ping);
181 
182     return 0;
183 }
184 
AddPendingBlob(uint32_t blobId,struct stream_renderer_resource_create_args blobArgs)185 int VirtioGpuContext::AddPendingBlob(uint32_t blobId,
186                                      struct stream_renderer_resource_create_args blobArgs) {
187     auto [_, inserted] = mPendingBlobs.try_emplace(blobId, blobArgs);
188     if (!inserted) {
189         stream_renderer_error(
190             "failed to add pending blob %u to context %u: blob ID already in use?", blobId, mId);
191         return -EINVAL;
192     }
193     return 0;
194 }
195 
TakePendingBlob(uint32_t blobId)196 std::optional<struct stream_renderer_resource_create_args> VirtioGpuContext::TakePendingBlob(
197     uint32_t blobId) {
198     auto it = mPendingBlobs.find(blobId);
199     if (it == mPendingBlobs.end()) {
200         return std::nullopt;
201     }
202 
203     auto args = it->second;
204     mPendingBlobs.erase(it);
205     return args;
206 }
207 
208 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
209 
210 using gfxstream::host::snapshot::VirtioGpuContextSnapshot;
211 
Snapshot() const212 std::optional<VirtioGpuContextSnapshot> VirtioGpuContext::Snapshot() const {
213     VirtioGpuContextSnapshot contextSnapshot;
214     contextSnapshot.set_id(mId);
215     contextSnapshot.set_name(mName);
216     contextSnapshot.set_capset(mCapsetId);
217     contextSnapshot.mutable_attached_resources()->Add(mAttachedResources.begin(),
218                                                       mAttachedResources.end());
219     contextSnapshot.mutable_resource_asgs()->insert(mAddressSpaceHandles.begin(),
220                                                     mAddressSpaceHandles.end());
221     return contextSnapshot;
222 }
223 
224 /*static*/
Restore(const VirtioGpuContextSnapshot & contextSnapshot)225 std::optional<VirtioGpuContext> VirtioGpuContext::Restore(
226     const VirtioGpuContextSnapshot& contextSnapshot) {
227     VirtioGpuContext context = {};
228     context.mId = contextSnapshot.id();
229     context.mName = contextSnapshot.name();
230     context.mCapsetId = contextSnapshot.capset();
231     context.mAttachedResources.insert(contextSnapshot.attached_resources().begin(),
232                                       contextSnapshot.attached_resources().end());
233     context.mAddressSpaceHandles.insert(contextSnapshot.resource_asgs().begin(),
234                                         contextSnapshot.resource_asgs().end());
235     return context;
236 }
237 
238 #endif
239 
240 }  // namespace host
241 }  // namespace gfxstream