// Copyright (C) 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "VirtioGpuContext.h" #include "host-common/AddressSpaceService.h" #include "host-common/opengles.h" namespace gfxstream { namespace host { /*static*/ std::optional VirtioGpuContext::Create(const GoldfishPipeServiceOps* ops, VirtioGpuContextId contextId, const std::string& contextName, uint32_t capsetId) { VirtioGpuContext context = {}; context.mId = contextId; context.mName = contextName; context.mCapsetId = capsetId; auto hostPipe = ops->guest_open_with_flags(reinterpret_cast(contextId), 0x1 /* is virtio */); if (!hostPipe) { stream_renderer_error("failed to create context %u: failed to create pipe.", contextId); return std::nullopt; } stream_renderer_debug("created initial pipe for context %u: %p", contextId, hostPipe); context.mHostPipe = hostPipe; android_onGuestGraphicsProcessCreate(contextId); return context; } int VirtioGpuContext::Destroy(const GoldfishPipeServiceOps* pipeOps, const struct address_space_device_control_ops* asgOps) { for (const auto& [_, handle] : mAddressSpaceHandles) { // Note: this can hang as is but this has only been observed to // happen during shutdown. See b/329287602#comment8. asgOps->destroy_handle(handle); } if (!mHostPipe) { stream_renderer_error("failed to destroy context %u: missing pipe?", mId); return -EINVAL; } pipeOps->guest_close(mHostPipe, GOLDFISH_PIPE_CLOSE_GRACEFUL); android_cleanupProcGLObjects(mId); return 0; } void VirtioGpuContext::AttachResource(VirtioGpuResource& resource) { // Associate the host pipe of the resource entry with the host pipe of // the context entry. That is, the last context to call attachResource // wins if there is any conflict. resource.AttachToContext(mId); resource.SetHostPipe(mHostPipe); mAttachedResources.insert(resource.GetId()); } void VirtioGpuContext::DetachResource(VirtioGpuResource& resource) { mAttachedResources.erase(resource.GetId()); resource.DetachFromContext(mId); } const std::unordered_set& VirtioGpuContext::GetAttachedResources() const { return mAttachedResources; } void VirtioGpuContext::SetHostPipe(GoldfishHostPipe* pipe) { mHostPipe = pipe; } int VirtioGpuContext::AcquireSync(uint64_t syncId) { if (mLatestSync) { stream_renderer_error( "failed to acquire sync %" PRIu64 " on context %u: sync already present?", syncId, mId); return -EINVAL; } auto descriptorOpt = ExternalObjectManager::get()->removeSyncDescriptorInfo(mId, syncId); if (!descriptorOpt) { stream_renderer_error("failed to acquire sync %" PRIu64 " on context %u: sync not found.", syncId, mId); return -EINVAL; } mLatestSync = std::move(*descriptorOpt); return 0; } std::optional VirtioGpuContext::TakeSync() { if (!mLatestSync) { return std::nullopt; } auto info = std::move(*mLatestSync); mLatestSync.reset(); return std::move(info); } int VirtioGpuContext::CreateAddressSpaceGraphicsInstance( const struct address_space_device_control_ops* asgOps, VirtioGpuResource& resource) { const VirtioGpuResourceId resourceId = resource.GetId(); void* resourceHva = nullptr; uint64_t resourceHvaSize = 0; if (resource.Map(&resourceHva, &resourceHvaSize) != 0) { stream_renderer_error( "failed to create ASG instance on context %d: failed to map resource %u", mId, resourceId); return -EINVAL; } const std::string asgName = mName + "-" + std::to_string(resourceId); // Note: resource ids can not be used as ASG handles because ASGs may outlive the // containing resource due asynchronous ASG destruction. const uint32_t asgId = asgOps->gen_handle(); struct AddressSpaceCreateInfo createInfo = { .handle = asgId, .type = android::emulation::VirtioGpuGraphics, .createRenderThread = true, .externalAddr = resourceHva, .externalAddrSize = resourceHvaSize, .virtioGpuContextId = mId, .virtioGpuCapsetId = mCapsetId, .contextName = asgName.c_str(), .contextNameSize = static_cast(asgName.size()), }; asgOps->create_instance(createInfo); mAddressSpaceHandles[resourceId] = asgId; return 0; } const std::unordered_map& VirtioGpuContext::AsgInstances() const { return mAddressSpaceHandles; } std::optional VirtioGpuContext::TakeAddressSpaceGraphicsHandle( VirtioGpuResourceId resourceId) { auto asgIt = mAddressSpaceHandles.find(resourceId); if (asgIt == mAddressSpaceHandles.end()) { return std::nullopt; } auto asgId = asgIt->second; mAddressSpaceHandles.erase(asgIt); return asgId; } int VirtioGpuContext::PingAddressSpaceGraphicsInstance( const struct address_space_device_control_ops* asgOps, VirtioGpuResourceId resourceId) { auto asgIt = mAddressSpaceHandles.find(resourceId); if (asgIt == mAddressSpaceHandles.end()) { stream_renderer_error( "failed to ping ASG instance on context %u resource %d: ASG not found.", mId, resourceId); return -EINVAL; } auto asgId = asgIt->second; struct android::emulation::AddressSpaceDevicePingInfo ping = {0}; ping.metadata = ASG_NOTIFY_AVAILABLE; asgOps->ping_at_hva(asgId, &ping); return 0; } int VirtioGpuContext::AddPendingBlob(uint32_t blobId, struct stream_renderer_resource_create_args blobArgs) { auto [_, inserted] = mPendingBlobs.try_emplace(blobId, blobArgs); if (!inserted) { stream_renderer_error( "failed to add pending blob %u to context %u: blob ID already in use?", blobId, mId); return -EINVAL; } return 0; } std::optional VirtioGpuContext::TakePendingBlob( uint32_t blobId) { auto it = mPendingBlobs.find(blobId); if (it == mPendingBlobs.end()) { return std::nullopt; } auto args = it->second; mPendingBlobs.erase(it); return args; } #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT using gfxstream::host::snapshot::VirtioGpuContextSnapshot; std::optional VirtioGpuContext::Snapshot() const { VirtioGpuContextSnapshot contextSnapshot; contextSnapshot.set_id(mId); contextSnapshot.set_name(mName); contextSnapshot.set_capset(mCapsetId); contextSnapshot.mutable_attached_resources()->Add(mAttachedResources.begin(), mAttachedResources.end()); contextSnapshot.mutable_resource_asgs()->insert(mAddressSpaceHandles.begin(), mAddressSpaceHandles.end()); return contextSnapshot; } /*static*/ std::optional VirtioGpuContext::Restore( const VirtioGpuContextSnapshot& contextSnapshot) { VirtioGpuContext context = {}; context.mId = contextSnapshot.id(); context.mName = contextSnapshot.name(); context.mCapsetId = contextSnapshot.capset(); context.mAttachedResources.insert(contextSnapshot.attached_resources().begin(), contextSnapshot.attached_resources().end()); context.mAddressSpaceHandles.insert(contextSnapshot.resource_asgs().begin(), contextSnapshot.resource_asgs().end()); return context; } #endif } // namespace host } // namespace gfxstream