blob: c2fcea69b4ff6ac012aabb083051d475de7a7c2f [file] [log] [blame] [edit]
// 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> 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<GoldfishHwPipe*>(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<VirtioGpuResourceId>& 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<SyncDescriptorInfo> 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<uint32_t>(asgName.size()),
};
asgOps->create_instance(createInfo);
mAddressSpaceHandles[resourceId] = asgId;
return 0;
}
const std::unordered_map<VirtioGpuResourceId, uint32_t>& VirtioGpuContext::AsgInstances() const {
return mAddressSpaceHandles;
}
std::optional<uint32_t> 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<struct stream_renderer_resource_create_args> 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<VirtioGpuContextSnapshot> 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> 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