/* * Copyright 2022 Google * SPDX-License-Identifier: MIT */ #include #include #include #include #include #include #include #include "VirtGpuKumquat.h" #include "util/log.h" #include "virtgpu_gfxstream_protocol.h" #define PARAM(x) \ (struct VirtGpuParam) { x, #x, 0 } static inline uint32_t align_up(uint32_t n, uint32_t a) { return ((n + a - 1) / a) * a; } VirtGpuKumquatDevice::VirtGpuKumquatDevice(enum VirtGpuCapset capset, int32_t descriptor) : VirtGpuDevice(capset) { struct VirtGpuParam params[] = { PARAM(VIRTGPU_KUMQUAT_PARAM_3D_FEATURES), PARAM(VIRTGPU_KUMQUAT_PARAM_CAPSET_QUERY_FIX), PARAM(VIRTGPU_KUMQUAT_PARAM_RESOURCE_BLOB), PARAM(VIRTGPU_KUMQUAT_PARAM_HOST_VISIBLE), PARAM(VIRTGPU_KUMQUAT_PARAM_CROSS_DEVICE), PARAM(VIRTGPU_KUMQUAT_PARAM_CONTEXT_INIT), PARAM(VIRTGPU_KUMQUAT_PARAM_SUPPORTED_CAPSET_IDs), PARAM(VIRTGPU_KUMQUAT_PARAM_EXPLICIT_DEBUG_NAME), PARAM(VIRTGPU_KUMQUAT_PARAM_FENCE_PASSING), PARAM(VIRTGPU_KUMQUAT_PARAM_CREATE_GUEST_HANDLE), }; int ret; struct drm_kumquat_get_caps get_caps = {0}; struct drm_kumquat_context_init init = {0}; struct drm_kumquat_context_set_param ctx_set_params[3] = {{0}}; const char* processName = nullptr; std::string gpu_socket_path = "/tmp/kumquat-gpu-"; memset(&mCaps, 0, sizeof(struct VirtGpuCaps)); #if DETECT_OS_ANDROID processName = getprogname(); #endif if (descriptor >= 0) { gpu_socket_path.append(std::to_string(descriptor)); mDescriptor = descriptor; } else { gpu_socket_path.append("0"); } ret = virtgpu_kumquat_init(&mVirtGpu, gpu_socket_path.c_str()); if (ret) { mesa_logi("Failed to init virtgpu kumquat"); return; } for (uint32_t i = 0; i < kParamMax; i++) { struct drm_kumquat_getparam get_param = {0}; get_param.param = params[i].param; ret = virtgpu_kumquat_get_param(mVirtGpu, &get_param); if (ret) { mesa_logi("virtgpu backend not enabling %s", params[i].name); continue; } mCaps.params[i] = get_param.value; } get_caps.cap_set_id = static_cast(capset); switch (capset) { case kCapsetGfxStreamVulkan: get_caps.size = sizeof(struct vulkanCapset); get_caps.addr = (unsigned long long)&mCaps.vulkanCapset; break; case kCapsetGfxStreamMagma: get_caps.size = sizeof(struct magmaCapset); get_caps.addr = (unsigned long long)&mCaps.magmaCapset; break; case kCapsetGfxStreamGles: get_caps.size = sizeof(struct vulkanCapset); get_caps.addr = (unsigned long long)&mCaps.glesCapset; break; case kCapsetGfxStreamComposer: get_caps.size = sizeof(struct vulkanCapset); get_caps.addr = (unsigned long long)&mCaps.composerCapset; break; default: get_caps.size = 0; } ret = virtgpu_kumquat_get_caps(mVirtGpu, &get_caps); if (ret) { // Don't fail get capabilities just yet, AEMU doesn't use this API // yet (b/272121235); mesa_loge("DRM_IOCTL_VIRTGPU_KUMQUAT_GET_CAPS failed with %s", strerror(errno)); } // We always need an ASG blob in some cases, so always define blobAlignment if (!mCaps.vulkanCapset.blobAlignment) { mCaps.vulkanCapset.blobAlignment = 4096; } ctx_set_params[0].param = VIRTGPU_KUMQUAT_CONTEXT_PARAM_NUM_RINGS; ctx_set_params[0].value = 2; init.num_params = 1; if (capset != kCapsetNone) { ctx_set_params[init.num_params].param = VIRTGPU_KUMQUAT_CONTEXT_PARAM_CAPSET_ID; ctx_set_params[init.num_params].value = static_cast(capset); init.num_params++; } if (mCaps.params[kParamExplicitDebugName] && processName) { ctx_set_params[init.num_params].param = VIRTGPU_KUMQUAT_CONTEXT_PARAM_DEBUG_NAME; ctx_set_params[init.num_params].value = reinterpret_cast(processName); init.num_params++; } init.ctx_set_params = (unsigned long long)&ctx_set_params[0]; ret = virtgpu_kumquat_context_init(mVirtGpu, &init); if (ret) { mesa_loge( "DRM_IOCTL_VIRTGPU_KUMQUAT_CONTEXT_INIT failed with %s, continuing without context...", strerror(errno)); } } VirtGpuKumquatDevice::~VirtGpuKumquatDevice() { virtgpu_kumquat_finish(&mVirtGpu); } struct VirtGpuCaps VirtGpuKumquatDevice::getCaps(void) { return mCaps; } int64_t VirtGpuKumquatDevice::getDeviceHandle(void) { return mDescriptor; } VirtGpuResourcePtr VirtGpuKumquatDevice::createResource(uint32_t width, uint32_t height, uint32_t stride, uint32_t size, uint32_t virglFormat, uint32_t target, uint32_t bind) { struct drm_kumquat_resource_create_3d create = { .target = target, .format = virglFormat, .bind = bind, .width = width, .height = height, .depth = 1U, .array_size = 1U, .last_level = 0, .nr_samples = 0, .size = size, .stride = stride, }; int ret = virtgpu_kumquat_resource_create_3d(mVirtGpu, &create); if (ret) { mesa_loge("DRM_IOCTL_VIRTGPU_KUMQUAT_RESOURCE_CREATE failed with %s", strerror(errno)); return nullptr; } return std::make_shared(mVirtGpu, create.bo_handle, create.res_handle, static_cast(create.size)); } VirtGpuResourcePtr VirtGpuKumquatDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) { int ret; struct drm_kumquat_resource_create_blob create = {0}; create.size = blobCreate.size; create.blob_mem = blobCreate.blobMem; create.blob_flags = blobCreate.flags; create.blob_id = blobCreate.blobId; create.cmd = (uint64_t)(uintptr_t)blobCreate.blobCmd; create.cmd_size = blobCreate.blobCmdSize; ret = virtgpu_kumquat_resource_create_blob(mVirtGpu, &create); if (ret < 0) { mesa_loge("DRM_VIRTGPU_KUMQUAT_RESOURCE_CREATE_BLOB failed with %s", strerror(errno)); return nullptr; } return std::make_shared(mVirtGpu, create.bo_handle, create.res_handle, blobCreate.size); } VirtGpuResourcePtr VirtGpuKumquatDevice::importBlob(const struct VirtGpuExternalHandle& handle) { int ret; struct drm_kumquat_resource_import resource_import = {0}; resource_import.os_handle = static_cast(handle.osHandle); resource_import.handle_type = static_cast(handle.type); ret = virtgpu_kumquat_resource_import(mVirtGpu, &resource_import); if (ret < 0) { mesa_loge("DRM_VIRTGPU_KUMQUAT_RESOURCE_IMPORT failed with %s", strerror(errno)); return nullptr; } return std::make_shared( mVirtGpu, resource_import.bo_handle, resource_import.res_handle, resource_import.size); } int VirtGpuKumquatDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuResource* blob) { int ret; struct drm_kumquat_execbuffer exec = {0}; uint32_t blobHandle; exec.flags = execbuffer.flags; exec.size = execbuffer.command_size; exec.ring_idx = execbuffer.ring_idx; exec.command = (uint64_t)(uintptr_t)(execbuffer.command); exec.fence_handle = -1; if (blob) { blobHandle = blob->getBlobHandle(); exec.bo_handles = (uint64_t)(uintptr_t)(&blobHandle); exec.num_bo_handles = 1; } ret = virtgpu_kumquat_execbuffer(mVirtGpu, &exec); if (ret) { mesa_loge("DRM_IOCTL_VIRTGPU_KUMQUAT_EXECBUFFER failed: %s", strerror(errno)); return ret; } if (execbuffer.flags & kFenceOut) { execbuffer.handle.osHandle = exec.fence_handle; execbuffer.handle.type = kFenceHandleSyncFd; } return 0; } VirtGpuDevice* kumquatCreateVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor) { return new VirtGpuKumquatDevice(capset, descriptor); }