diff options
Diffstat (limited to 'src/gfxstream/guest/platform/rutabaga')
9 files changed, 1500 insertions, 0 deletions
diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaLayer.cpp b/src/gfxstream/guest/platform/rutabaga/RutabagaLayer.cpp new file mode 100644 index 00000000000..42fb57dc8a6 --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaLayer.cpp @@ -0,0 +1,1035 @@ +/* + * Copyright 2023 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 "RutabagaLayer.h" + +#include <inttypes.h> +#include <log/log.h> + +#include <cstdlib> +#include <future> +#include <memory> +#include <optional> +#include <queue> +#include <sstream> +#include <string> +#include <thread> +#include <unordered_map> +#include <variant> + +#include "gfxstream/virtio-gpu-gfxstream-renderer.h" +#include "gfxstream/virtio-gpu-gfxstream-renderer-unstable.h" + +namespace gfxstream { +namespace { + +constexpr const uint32_t kInvalidContextId = 0; + +std::vector<std::string> Split(const std::string& s, const std::string& delimiters) { + if (delimiters.empty()) { + return {}; + } + + std::vector<std::string> result; + + size_t base = 0; + size_t found; + while (true) { + found = s.find_first_of(delimiters, base); + result.push_back(s.substr(base, found - base)); + if (found == s.npos) break; + base = found + 1; + } + + return result; +} + +std::string Join(const std::vector<std::string>& things, const std::string& separator) { + if (things.empty()) { + return ""; + } + + std::ostringstream result; + result << *things.begin(); + for (auto it = std::next(things.begin()); it != things.end(); ++it) { + result << separator << *it; + } + return result.str(); +} + +} // namespace + +class EmulatedVirtioGpu::EmulatedVirtioGpuImpl { + public: + EmulatedVirtioGpuImpl(); + ~EmulatedVirtioGpuImpl(); + + bool Init(bool withGl, bool withVk, bool withVkSnapshots, EmulatedVirtioGpu* parent); + + VirtGpuCaps GetCaps(VirtGpuCapset capset); + + std::optional<uint32_t> CreateContext(); + void DestroyContext(uint32_t contextId); + + uint8_t* Map(uint32_t resourceId); + void Unmap(uint32_t resourceId); + + int ExecBuffer(uint32_t contextId, struct VirtGpuExecBuffer& execbuffer, + std::optional<uint32_t> blobResourceId); + + int Wait(uint32_t resourceId); + + int TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset, + uint32_t transferSize); + int TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset, + uint32_t transferSize); + + std::optional<uint32_t> CreateBlob(uint32_t contextId, const struct VirtGpuCreateBlob& params); + std::optional<uint32_t> CreateVirglBlob(uint32_t contextId, uint32_t width, uint32_t height, + uint32_t virglFormat); + + void DestroyResource(uint32_t contextId, uint32_t resourceId); + + uint32_t CreateEmulatedFence(); + + void SignalEmulatedFence(uint32_t fenceId); + + int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds); + + private: + struct VirtioGpuTaskContextAttachResource { + uint32_t contextId; + uint32_t resourceId; + }; + struct VirtioGpuTaskContextDetachResource { + uint32_t contextId; + uint32_t resourceId; + }; + struct VirtioGpuTaskCreateContext { + uint32_t contextId; + uint32_t contextInit; + std::string contextName; + }; + struct VirtioGpuTaskCreateBlob { + uint32_t contextId; + uint32_t resourceId; + struct stream_renderer_create_blob params; + }; + struct VirtioGpuTaskCreateResource { + uint32_t contextId; + uint32_t resourceId; + uint8_t* resourceBytes; + struct stream_renderer_resource_create_args params; + }; + struct VirtioGpuTaskDestroyContext { + uint32_t contextId; + }; + struct VirtioGpuTaskMap { + uint32_t resourceId; + std::promise<uint8_t*> resourceMappedPromise; + }; + struct VirtioGpuTaskExecBuffer { + uint32_t contextId; + std::vector<std::byte> commandBuffer; + }; + struct VirtioGpuTaskTransferToHost { + uint32_t contextId; + uint32_t resourceId; + uint32_t transferOffset; + uint32_t transferSize; + }; + struct VirtioGpuTaskTransferFromHost { + uint32_t contextId; + uint32_t resourceId; + uint32_t transferOffset; + uint32_t transferSize; + }; + struct VirtioGpuTaskUnrefResource { + uint32_t resourceId; + }; + using VirtioGpuTask = + std::variant<VirtioGpuTaskContextAttachResource, VirtioGpuTaskContextDetachResource, + VirtioGpuTaskCreateBlob, VirtioGpuTaskCreateContext, + VirtioGpuTaskCreateResource, VirtioGpuTaskDestroyContext, VirtioGpuTaskMap, + VirtioGpuTaskExecBuffer, VirtioGpuTaskTransferFromHost, + VirtioGpuTaskTransferToHost, VirtioGpuTaskUnrefResource>; + struct VirtioGpuTaskWithWaitable { + uint32_t contextId; + VirtioGpuTask task; + std::promise<void> taskCompletedSignaler; + std::optional<uint32_t> fence; + }; + + std::shared_future<void> EnqueueVirtioGpuTask(uint32_t contextId, VirtioGpuTask task, + std::optional<uint32_t> fence = std::nullopt); + void DoTask(VirtioGpuTaskContextAttachResource task); + void DoTask(VirtioGpuTaskContextDetachResource task); + void DoTask(VirtioGpuTaskCreateContext task); + void DoTask(VirtioGpuTaskCreateBlob task); + void DoTask(VirtioGpuTaskCreateResource task); + void DoTask(VirtioGpuTaskDestroyContext task); + void DoTask(VirtioGpuTaskMap task); + void DoTask(VirtioGpuTaskExecBuffer task); + void DoTask(VirtioGpuTaskTransferFromHost task); + void DoTask(VirtioGpuTaskTransferToHost task); + void DoTask(VirtioGpuTaskWithWaitable task); + void DoTask(VirtioGpuTaskUnrefResource task); + + void RunVirtioGpuTaskProcessingLoop(); + + std::atomic<uint32_t> mNextContextId{1}; + std::atomic<uint32_t> mNextVirtioGpuResourceId{1}; + std::atomic<uint32_t> mNextVirtioGpuFenceId{1}; + + std::atomic_bool mShuttingDown{false}; + + std::mutex mTasksMutex; + std::queue<VirtioGpuTaskWithWaitable> mTasks; + + enum class EmulatedResourceType { + kBlob, + kPipe, + }; + struct EmulatedResource { + EmulatedResourceType type; + + std::mutex pendingWaitablesMutex; + std::vector<std::shared_future<void>> pendingWaitables; + + // For non-blob resources, the guest shadow memory. + std::unique_ptr<uint8_t[]> guestBytes; + + // For mappable blob resources, the host memory once it is mapped. + std::shared_future<uint8_t*> mappedHostBytes; + }; + std::mutex mResourcesMutex; + std::unordered_map<uint32_t, EmulatedResource> mResources; + + EmulatedResource* CreateResource(uint32_t resourceId, EmulatedResourceType resourceType) { + std::lock_guard<std::mutex> lock(mResourcesMutex); + + auto [it, created] = mResources.emplace( + std::piecewise_construct, std::forward_as_tuple(resourceId), std::forward_as_tuple()); + if (!created) { + ALOGE("Created resource %" PRIu32 " twice?", resourceId); + } + + EmulatedResource* resource = &it->second; + resource->type = resourceType; + return resource; + } + + EmulatedResource* GetResource(uint32_t resourceId) { + std::lock_guard<std::mutex> lock(mResourcesMutex); + + auto it = mResources.find(resourceId); + if (it == mResources.end()) { + return nullptr; + } + + return &it->second; + } + + void DeleteResource(uint32_t resourceId) { + std::lock_guard<std::mutex> lock(mResourcesMutex); + mResources.erase(resourceId); + } + + struct EmulatedFence { + std::promise<void> signaler; + std::shared_future<void> waitable; + }; + std::mutex mVirtioGpuFencesMutex; + std::unordered_map<uint32_t, EmulatedFence> mVirtioGpuFences; + + std::thread mWorkerThread; +}; + +EmulatedVirtioGpu::EmulatedVirtioGpuImpl::EmulatedVirtioGpuImpl() + : mWorkerThread([this]() { RunVirtioGpuTaskProcessingLoop(); }) {} + +EmulatedVirtioGpu::EmulatedVirtioGpuImpl::~EmulatedVirtioGpuImpl() { + mShuttingDown = true; + mWorkerThread.join(); + + stream_renderer_teardown(); +} + +namespace { + +void WriteFenceTrampoline(void* cookie, struct stream_renderer_fence* fence) { + auto* gpu = reinterpret_cast<EmulatedVirtioGpu*>(cookie); + gpu->SignalEmulatedFence(fence->fence_id); +} + +} // namespace + +bool EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Init(bool withGl, bool withVk, bool withVkSnapshots, + EmulatedVirtioGpu* parent) { + std::vector<stream_renderer_param> renderer_params{ + stream_renderer_param{ + .key = STREAM_RENDERER_PARAM_USER_DATA, + .value = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(parent)), + }, + stream_renderer_param{ + .key = STREAM_RENDERER_PARAM_FENCE_CALLBACK, + .value = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&WriteFenceTrampoline)), + }, + stream_renderer_param{ + .key = STREAM_RENDERER_PARAM_RENDERER_FLAGS, + .value = + static_cast<uint64_t>(STREAM_RENDERER_FLAGS_USE_SURFACELESS_BIT) | + (withGl ? static_cast<uint64_t>(STREAM_RENDERER_FLAGS_USE_EGL_BIT | + STREAM_RENDERER_FLAGS_USE_GLES_BIT) + : 0) | + (withVk ? static_cast<uint64_t>(STREAM_RENDERER_FLAGS_USE_VK_BIT) : 0) | + (withVkSnapshots ? static_cast<uint64_t>(STREAM_RENDERER_FLAGS_VULKAN_SNAPSHOTS) + : 0), + }, + stream_renderer_param{ + .key = STREAM_RENDERER_PARAM_WIN0_WIDTH, + .value = 32, + }, + stream_renderer_param{ + .key = STREAM_RENDERER_PARAM_WIN0_HEIGHT, + .value = 32, + }, + }; + return stream_renderer_init(renderer_params.data(), renderer_params.size()) == 0; +} + +VirtGpuCaps EmulatedVirtioGpu::EmulatedVirtioGpuImpl::GetCaps(VirtGpuCapset capset) { + VirtGpuCaps caps = { + .params = + { + [kParam3D] = 1, + [kParamCapsetFix] = 1, + [kParamResourceBlob] = 1, + [kParamHostVisible] = 1, + [kParamCrossDevice] = 0, + [kParamContextInit] = 1, + [kParamSupportedCapsetIds] = 0, + [kParamCreateGuestHandle] = 0, + }, + }; + + stream_renderer_fill_caps(static_cast<uint32_t>(capset), 0, &caps.vulkanCapset); + + return caps; +} + +std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateContext() { + const uint32_t contextId = mNextContextId++; + + VirtioGpuTaskCreateContext task = { + .contextId = contextId, + .contextInit = 0, + .contextName = "EmulatedVirtioGpu Context " + std::to_string(contextId), + }; + EnqueueVirtioGpuTask(contextId, std::move(task)); + return contextId; +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DestroyContext(uint32_t contextId) { + VirtioGpuTaskDestroyContext task = { + .contextId = contextId, + }; + EnqueueVirtioGpuTask(contextId, std::move(task)); +} + +uint8_t* EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Map(uint32_t resourceId) { + EmulatedResource* resource = GetResource(resourceId); + if (resource == nullptr) { + ALOGE("Failed to Map() resource %" PRIu32 ": not found.", resourceId); + return nullptr; + } + + uint8_t* mapped = nullptr; + if (resource->type == EmulatedResourceType::kBlob) { + if (!resource->mappedHostBytes.valid()) { + ALOGE("Failed to Map() resource %" PRIu32 + ": attempting to map blob " + "without mappable flag?", + resourceId); + return nullptr; + } + mapped = resource->mappedHostBytes.get(); + } else if (resource->type == EmulatedResourceType::kPipe) { + mapped = resource->guestBytes.get(); + } + return mapped; +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Unmap(uint32_t resourceId) { + stream_renderer_resource_unmap(resourceId); +} + +int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::Wait(uint32_t resourceId) { + EmulatedResource* resource = GetResource(resourceId); + if (resource == nullptr) { + ALOGE("Failed to Wait() on resource %" PRIu32 ": not found.", resourceId); + return -1; + } + + std::vector<std::shared_future<void>> pendingWaitables; + { + std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex); + pendingWaitables = resource->pendingWaitables; + resource->pendingWaitables.clear(); + } + for (auto& waitable : pendingWaitables) { + waitable.wait(); + } + + return 0; +} + +int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferFromHost(uint32_t contextId, + uint32_t resourceId, + uint32_t transferOffset, + uint32_t transferSize) { + EmulatedResource* resource = GetResource(resourceId); + if (resource == nullptr) { + ALOGE("Failed to TransferFromHost() on resource %" PRIu32 ": not found.", resourceId); + return -1; + } + + VirtioGpuTaskTransferFromHost task = { + .contextId = contextId, + .resourceId = resourceId, + .transferOffset = transferOffset, + .transferSize = transferSize, + }; + auto waitable = EnqueueVirtioGpuTask(contextId, std::move(task)); + + { + std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex); + resource->pendingWaitables.push_back(std::move(waitable)); + } + + return 0; +} + +int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::TransferToHost(uint32_t contextId, + uint32_t resourceId, + uint32_t transferOffset, + uint32_t transferSize) { + EmulatedResource* resource = GetResource(resourceId); + if (resource == nullptr) { + ALOGE("Failed to TransferFromHost() on resource %" PRIu32 ": not found.", resourceId); + return -1; + } + + VirtioGpuTaskTransferToHost task = { + .contextId = contextId, + .resourceId = resourceId, + .transferOffset = transferOffset, + .transferSize = transferSize, + }; + auto waitable = EnqueueVirtioGpuTask(contextId, std::move(task)); + + { + std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex); + resource->pendingWaitables.push_back(std::move(waitable)); + } + + return 0; +} + +std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateBlob( + uint32_t contextId, const struct VirtGpuCreateBlob& blobCreate) { + + const uint32_t resourceId = mNextVirtioGpuResourceId++; + + ALOGV("Enquing task to create blob resource-id:%d size:%" PRIu64, resourceId, blobCreate.size); + + EmulatedResource* resource = CreateResource(resourceId, EmulatedResourceType::kBlob); + + VirtioGpuTaskCreateBlob createTask{ + .contextId = contextId, + .resourceId = resourceId, + .params = + { + .blob_mem = static_cast<uint32_t>(blobCreate.blobMem), + .blob_flags = static_cast<uint32_t>(blobCreate.flags), + .blob_id = blobCreate.blobId, + .size = blobCreate.size, + }, + }; + auto createBlobCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(createTask)); + resource->pendingWaitables.push_back(std::move(createBlobCompletedWaitable)); + + if (blobCreate.flags & kBlobFlagMappable) { + std::promise<uint8_t*> mappedBytesPromise; + std::shared_future<uint8_t*> mappedBytesWaitable = mappedBytesPromise.get_future(); + + VirtioGpuTaskMap mapTask{ + .resourceId = resourceId, + .resourceMappedPromise = std::move(mappedBytesPromise), + }; + EnqueueVirtioGpuTask(contextId, std::move(mapTask)); + resource->mappedHostBytes = std::move(mappedBytesWaitable); + } + + VirtioGpuTaskContextAttachResource attachTask{ + .contextId = contextId, + .resourceId = resourceId, + }; + EnqueueVirtioGpuTask(contextId, std::move(attachTask)); + + return resourceId; +} + +std::optional<uint32_t> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateVirglBlob( + uint32_t contextId, uint32_t width, uint32_t height, uint32_t virglFormat) { + + const uint32_t resourceId = mNextVirtioGpuResourceId++; + + EmulatedResource* resource = CreateResource(resourceId, EmulatedResourceType::kPipe); + + uint32_t target = 0; + uint32_t bind = 0; + uint32_t bpp = 0; + + switch (virglFormat) { + case VIRGL_FORMAT_R8G8B8A8_UNORM: + case VIRGL_FORMAT_B8G8R8A8_UNORM: + target = PIPE_TEXTURE_2D; + bind = VIRGL_BIND_RENDER_TARGET; + bpp = 4; + break; + case VIRGL_FORMAT_B5G6R5_UNORM: + target = PIPE_TEXTURE_2D; + bind = VIRGL_BIND_RENDER_TARGET; + bpp = 2; + break; + case VIRGL_FORMAT_R8G8B8_UNORM: + target = PIPE_TEXTURE_2D; + bind = VIRGL_BIND_RENDER_TARGET; + bpp = 3; + break; + case VIRGL_FORMAT_R8_UNORM: + target = PIPE_BUFFER; + bind = VIRGL_BIND_CUSTOM; + bpp = 1; + break; + default: + ALOGE("Unknown virgl format %u", virglFormat); + return {}; + } + + resource->guestBytes = std::make_unique<uint8_t[]>(width * height * bpp); + + VirtioGpuTaskCreateResource task{ + .contextId = contextId, + .resourceId = resourceId, + .resourceBytes = resource->guestBytes.get(), + .params = + { + .handle = resourceId, + .target = target, + .format = virglFormat, + .bind = bind, + .width = width, + .height = height, + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + }, + }; + auto taskCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(task)); + resource->pendingWaitables.push_back(std::move(taskCompletedWaitable)); + + VirtioGpuTaskContextAttachResource attachTask{ + .contextId = contextId, + .resourceId = resourceId, + }; + EnqueueVirtioGpuTask(contextId, std::move(attachTask)); + + return resourceId; +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DestroyResource(uint32_t contextId, + uint32_t resourceId) { + DeleteResource(resourceId); + + VirtioGpuTaskUnrefResource unrefTask{ + .resourceId = resourceId, + }; + EnqueueVirtioGpuTask(contextId, std::move(unrefTask)); + + VirtioGpuTaskContextDetachResource detachTask{ + .contextId = contextId, + .resourceId = resourceId, + }; + EnqueueVirtioGpuTask(contextId, std::move(detachTask)); +} + +int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::ExecBuffer(uint32_t contextId, + struct VirtGpuExecBuffer& execbuffer, + std::optional<uint32_t> blobResourceId) { + std::optional<uint32_t> fence; + + if (execbuffer.flags & kFenceOut) { + fence = CreateEmulatedFence(); + } + + const VirtioGpuTaskExecBuffer task = { + .contextId = contextId, + .commandBuffer = std::vector<std::byte>( + reinterpret_cast<std::byte*>(execbuffer.command), + reinterpret_cast<std::byte*>(execbuffer.command) + execbuffer.command_size), + }; + auto taskCompletedWaitable = EnqueueVirtioGpuTask(contextId, std::move(task), fence); + + if (blobResourceId) { + EmulatedResource* resource = GetResource(*blobResourceId); + if (resource == nullptr) { + ALOGE("Failed to ExecBuffer() with resource %" PRIu32 ": not found.", *blobResourceId); + return -1; + } + + { + std::lock_guard<std::mutex> lock(resource->pendingWaitablesMutex); + resource->pendingWaitables.push_back(std::move(taskCompletedWaitable)); + } + } + + if (execbuffer.flags & kFenceOut) { + execbuffer.handle.osHandle = *fence; + execbuffer.handle.type = kFenceHandleSyncFd; + } + + return 0; +} + +int EmulatedVirtioGpu::EmulatedVirtioGpuImpl::WaitOnEmulatedFence(int fenceAsFileDescriptor, + int timeoutMilliseconds) { + uint32_t fenceId = static_cast<uint32_t>(fenceAsFileDescriptor); + ALOGV("Waiting on fence:%d", (int)fenceId); + + std::shared_future<void> waitable; + + { + std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex); + + auto fenceIt = mVirtioGpuFences.find(fenceId); + if (fenceIt == mVirtioGpuFences.end()) { + ALOGE("Fence:%d already signaled", (int)fenceId); + return 0; + } + auto& fence = fenceIt->second; + + waitable = fence.waitable; + } + + auto status = waitable.wait_for(std::chrono::milliseconds(timeoutMilliseconds)); + if (status == std::future_status::ready) { + ALOGV("Finished waiting for fence:%d", (int)fenceId); + return 0; + } else { + ALOGE("Timed out waiting for fence:%d", (int)fenceId); + return -1; + } +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::SignalEmulatedFence(uint32_t fenceId) { + ALOGV("Signaling fence:%d", (int)fenceId); + + std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex); + + auto fenceIt = mVirtioGpuFences.find(fenceId); + if (fenceIt == mVirtioGpuFences.end()) { + ALOGE("Failed to find fence %" PRIu32, fenceId); + return; + } + auto& fenceInfo = fenceIt->second; + fenceInfo.signaler.set_value(); +} + +uint32_t EmulatedVirtioGpu::EmulatedVirtioGpuImpl::CreateEmulatedFence() { + const uint32_t fenceId = mNextVirtioGpuFenceId++; + + ALOGV("Creating fence:%d", (int)fenceId); + + std::lock_guard<std::mutex> lock(mVirtioGpuFencesMutex); + + auto [fenceIt, fenceCreated] = mVirtioGpuFences.emplace(fenceId, EmulatedFence{}); + if (!fenceCreated) { + ALOGE("Attempting to recreate fence %" PRIu32, fenceId); + } + + auto& fenceInfo = fenceIt->second; + fenceInfo.waitable = fenceInfo.signaler.get_future(); + + return fenceId; +} + +std::shared_future<void> EmulatedVirtioGpu::EmulatedVirtioGpuImpl::EnqueueVirtioGpuTask( + uint32_t contextId, VirtioGpuTask task, std::optional<uint32_t> fence) { + std::promise<void> taskCompletedSignaler; + std::shared_future<void> taskCompletedWaitable(taskCompletedSignaler.get_future()); + + std::lock_guard<std::mutex> lock(mTasksMutex); + mTasks.push(VirtioGpuTaskWithWaitable{ + .contextId = contextId, + .task = std::move(task), + .taskCompletedSignaler = std::move(taskCompletedSignaler), + .fence = fence, + }); + + return taskCompletedWaitable; +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskContextAttachResource task) { + ALOGV("Performing task to attach resource-id:%d to context-id:%d", task.resourceId, + task.contextId); + + stream_renderer_ctx_attach_resource(task.contextId, task.resourceId); + + ALOGV("Performing task to attach resource-id:%d to context-id:%d - done", task.resourceId, + task.contextId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskContextDetachResource task) { + ALOGV("Performing task to detach resource-id:%d to context-id:%d", task.resourceId, + task.contextId); + + stream_renderer_ctx_detach_resource(task.contextId, task.resourceId); + + ALOGV("Performing task to detach resource-id:%d to context-id:%d - done", task.resourceId, + task.contextId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateBlob task) { + ALOGV("Performing task to create blob resource-id:%d", task.resourceId); + + int ret = stream_renderer_create_blob(task.contextId, task.resourceId, &task.params, + /*iovecs=*/nullptr, + /*num_iovs=*/0, + /*handle=*/nullptr); + if (ret) { + ALOGE("Failed to create blob."); + } + + ALOGV("Performing task to create blob resource-id:%d - done", task.resourceId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateContext task) { + ALOGV("Performing task to create context-id:%" PRIu32 " context-init:%" PRIu32 + " context-name:%s", + task.contextId, task.contextInit, task.contextName.c_str()); + + int ret = stream_renderer_context_create(task.contextId, task.contextName.size(), + task.contextName.data(), task.contextInit); + if (ret) { + ALOGE("Failed to create context-id:%" PRIu32 ".", task.contextId); + return; + } + + ALOGV("Performing task to create context-id:%" PRIu32 " context-init:%" PRIu32 + " context-name:%s - done", + task.contextId, task.contextInit, task.contextName.c_str()); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskCreateResource task) { + ALOGV("Performing task to create resource resource:%d", task.resourceId); + + int ret = stream_renderer_resource_create(&task.params, nullptr, 0); + if (ret) { + ALOGE("Failed to create resource:%d", task.resourceId); + } + + struct iovec iov = { + .iov_base = task.resourceBytes, + .iov_len = task.params.width, + }; + ret = stream_renderer_resource_attach_iov(task.resourceId, &iov, 1); + if (ret) { + ALOGE("Failed to attach iov to resource:%d", task.resourceId); + } + + ALOGV("Performing task to create resource resource:%d - done", task.resourceId); + + stream_renderer_ctx_attach_resource(task.contextId, task.resourceId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskDestroyContext task) { + ALOGV("Performing task to destroy context-id:%" PRIu32, task.contextId); + + stream_renderer_context_destroy(task.contextId); + + ALOGV("Performing task to destroy context-id:%" PRIu32 " - done", task.contextId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskMap task) { + ALOGV("Performing task to map resource resource:%d", task.resourceId); + + void* mapped = nullptr; + + int ret = stream_renderer_resource_map(task.resourceId, &mapped, nullptr); + if (ret) { + ALOGE("Failed to map resource:%d", task.resourceId); + return; + } + + task.resourceMappedPromise.set_value(reinterpret_cast<uint8_t*>(mapped)); + ALOGV("Performing task to map resource resource:%d - done", task.resourceId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskExecBuffer task) { + ALOGV("Performing task to execbuffer"); + + if (task.commandBuffer.size() % 4 != 0) { + ALOGE("Unaligned command buffer?"); + return; + } + + stream_renderer_command cmd = { + .ctx_id = task.contextId, + .cmd_size = static_cast<uint32_t>(task.commandBuffer.size()), + .cmd = reinterpret_cast<uint8_t*>(task.commandBuffer.data()), + .num_in_fences = 0, + .fences = nullptr, + }; + + int ret = stream_renderer_submit_cmd(&cmd); + if (ret) { + ALOGE("Failed to execbuffer."); + } + + ALOGV("Performing task to execbuffer - done"); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskTransferFromHost task) { + struct stream_renderer_box transferBox = { + .x = task.transferOffset, + .y = 0, + .z = 0, + .w = task.transferSize, + .h = 1, + .d = 1, + }; + + int ret = stream_renderer_transfer_read_iov(task.resourceId, task.contextId, + /*level=*/0, + /*stride=*/0, + /*layer_stride=*/0, &transferBox, + /*offset=*/0, + /*iov=*/nullptr, + /*iovec_cnt=*/0); + if (ret) { + ALOGE("Failed to transferFromHost() for resource:%" PRIu32, task.resourceId); + } +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskTransferToHost task) { + struct stream_renderer_box transferBox = { + .x = task.transferOffset, + .y = 0, + .z = 0, + .w = task.transferSize, + .h = 1, + .d = 1, + }; + + int ret = stream_renderer_transfer_write_iov(task.resourceId, task.contextId, + /*level=*/0, + /*stride=*/0, + /*layer_stride=*/0, &transferBox, + /*offset=*/0, + /*iov=*/nullptr, + /*iovec_cnt=*/0); + if (ret) { + ALOGE("Failed to transferToHost() for resource:%" PRIu32, task.resourceId); + } +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskUnrefResource task) { + stream_renderer_resource_unref(task.resourceId); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::DoTask(VirtioGpuTaskWithWaitable task) { + std::visit( + [this](auto&& work) { + using T = std::decay_t<decltype(work)>; + if constexpr (std::is_same_v<T, VirtioGpuTaskContextAttachResource>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskContextDetachResource>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateBlob>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateContext>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskCreateResource>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskDestroyContext>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskMap>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskExecBuffer>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskTransferFromHost>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskTransferToHost>) { + DoTask(std::move(work)); + } else if constexpr (std::is_same_v<T, VirtioGpuTaskUnrefResource>) { + DoTask(std::move(work)); + } + }, + task.task); + + if (task.fence) { + const stream_renderer_fence fenceInfo = { + .flags = STREAM_RENDERER_FLAG_FENCE_RING_IDX, + .fence_id = *task.fence, + .ctx_id = task.contextId, + .ring_idx = 0, + }; + int ret = stream_renderer_create_fence(&fenceInfo); + if (ret) { + ALOGE("Failed to create fence."); + } + } + + task.taskCompletedSignaler.set_value(); +} + +void EmulatedVirtioGpu::EmulatedVirtioGpuImpl::RunVirtioGpuTaskProcessingLoop() { + while (!mShuttingDown.load()) { + std::optional<VirtioGpuTaskWithWaitable> task; + + { + std::lock_guard<std::mutex> lock(mTasksMutex); + if (!mTasks.empty()) { + task = std::move(mTasks.front()); + mTasks.pop(); + } + } + + if (task) { + DoTask(std::move(*task)); + } + } +} + +namespace { + +EmulatedVirtioGpu* sInstance = nullptr; + +} // namespace + +EmulatedVirtioGpu::EmulatedVirtioGpu() + : mImpl{std::make_unique<EmulatedVirtioGpu::EmulatedVirtioGpuImpl>()} {} + +/*static*/ +EmulatedVirtioGpu& EmulatedVirtioGpu::Get() { + if (sInstance == nullptr) { + sInstance = new EmulatedVirtioGpu(); + + bool withGl = false; + bool withVk = true; + bool withVkSnapshots = false; + + struct Option { + std::string env; + bool* val; + }; + const std::vector<Option> options = { + {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl}, + {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk}, + {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK_SNAPSHOTS", &withVkSnapshots}, + }; + for (const Option option : options) { + const char* val = std::getenv(option.env.c_str()); + if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) { + *option.val = true; + } + } + + ALOGE("Initializing withGl:%d withVk:%d withVkSnapshots:%d", withGl, withVk, + withVkSnapshots); + if (!sInstance->Init(withGl, withVk, withVkSnapshots)) { + ALOGE("Failed to initialize EmulatedVirtioGpu."); + } + } + return *sInstance; +} + +/*static*/ +void EmulatedVirtioGpu::Reset() { + if (sInstance != nullptr) { + delete sInstance; + sInstance = nullptr; + } +} + +bool EmulatedVirtioGpu::Init(bool withGl, bool withVk, bool withVkSnapshots) { + return mImpl->Init(withGl, withVk, withVkSnapshots, this); +} + +std::optional<uint32_t> EmulatedVirtioGpu::CreateContext() { return mImpl->CreateContext(); } + +void EmulatedVirtioGpu::DestroyContext(uint32_t contextId) { mImpl->DestroyContext(contextId); } + +VirtGpuCaps EmulatedVirtioGpu::GetCaps(VirtGpuCapset capset) { return mImpl->GetCaps(capset); } + +uint8_t* EmulatedVirtioGpu::Map(uint32_t resourceId) { return mImpl->Map(resourceId); } + +void EmulatedVirtioGpu::Unmap(uint32_t resourceId) { mImpl->Unmap(resourceId); } + +int EmulatedVirtioGpu::ExecBuffer(uint32_t contextId, struct VirtGpuExecBuffer& execbuffer, + std::optional<uint32_t> blobResourceId) { + return mImpl->ExecBuffer(contextId, execbuffer, blobResourceId); +} + +int EmulatedVirtioGpu::Wait(uint32_t resourceId) { return mImpl->Wait(resourceId); } + +int EmulatedVirtioGpu::TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t offset, + uint32_t size) { + return mImpl->TransferFromHost(contextId, resourceId, offset, size); +} + +int EmulatedVirtioGpu::TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t offset, + uint32_t size) { + return mImpl->TransferToHost(contextId, resourceId, offset, size); +} + +std::optional<uint32_t> EmulatedVirtioGpu::CreateBlob(uint32_t contextId, + const struct VirtGpuCreateBlob& params) { + return mImpl->CreateBlob(contextId, params); +} + +std::optional<uint32_t> EmulatedVirtioGpu::CreateVirglBlob(uint32_t contextId, uint32_t width, + uint32_t height, uint32_t virglFormat) { + return mImpl->CreateVirglBlob(contextId, width, height, virglFormat); +} + +void EmulatedVirtioGpu::DestroyResource(uint32_t contextId, uint32_t resourceId) { + mImpl->DestroyResource(contextId, resourceId); +} + +int EmulatedVirtioGpu::WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds) { + return mImpl->WaitOnEmulatedFence(fenceAsFileDescriptor, timeoutMilliseconds); +} + +void EmulatedVirtioGpu::SignalEmulatedFence(int fenceId) { mImpl->SignalEmulatedFence(fenceId); } + +void ResetEmulatedVirtioGpu() { EmulatedVirtioGpu::Reset(); } + +} // namespace gfxstream diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaLayer.h b/src/gfxstream/guest/platform/rutabaga/RutabagaLayer.h new file mode 100644 index 00000000000..057b2f7575e --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaLayer.h @@ -0,0 +1,80 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <memory> +#include <optional> + +#include "VirtGpu.h" + +namespace gfxstream { + +// Emulates parts of the Linux Virtio GPU kernel module and parts of +// a virtual machine manager to allow speaking directly to the Gfxstream +// host server via rutabaga. +class EmulatedVirtioGpu { + public: + static EmulatedVirtioGpu& Get(); + static void Reset(); + + bool Init(bool withGl, bool withVk, bool withVkSnapshots); + + VirtGpuCaps GetCaps(VirtGpuCapset capset); + + std::optional<uint32_t> CreateContext(); + void DestroyContext(uint32_t contextId); + + std::optional<uint32_t> CreateBlob(uint32_t contextId, + const struct VirtGpuCreateBlob& params); + std::optional<uint32_t> CreateVirglBlob(uint32_t contextId, + uint32_t width, + uint32_t height, + uint32_t virglFormat); + + void DestroyResource(uint32_t contextId, + uint32_t resourceId); + + uint8_t* Map(uint32_t resourceId); + void Unmap(uint32_t resourceId); + + int ExecBuffer(uint32_t contextId, + struct VirtGpuExecBuffer& execbuffer, + std::optional<uint32_t> blobResourceId); + + int Wait(uint32_t resourceId); + + int TransferFromHost(uint32_t contextId, + uint32_t resourceId, + uint32_t transferOffset, + uint32_t transferSize); + int TransferToHost(uint32_t contextId, + uint32_t resourceId, + uint32_t transferOffset, + uint32_t transferSize); + + void SignalEmulatedFence(int fenceId); + + int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds); + + private: + EmulatedVirtioGpu(); + + class EmulatedVirtioGpuImpl; + std::unique_ptr<EmulatedVirtioGpuImpl> mImpl; +}; + +} // namespace gfxstream diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpu.h b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpu.h new file mode 100644 index 00000000000..8cf07f5f17b --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpu.h @@ -0,0 +1,99 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <memory> + +#include "VirtGpu.h" + +namespace gfxstream { + +// Virtio GPU abstraction that directly runs a host render server. + +class RutabagaVirtGpuDevice; + +class RutabagaVirtGpuBlobMapping : public VirtGpuBlobMapping { + public: + RutabagaVirtGpuBlobMapping(VirtGpuBlobPtr blob, uint8_t* mapped); + ~RutabagaVirtGpuBlobMapping(); + + uint8_t* asRawPtr(void) override; + + private: + VirtGpuBlobPtr mBlob; + uint8_t* mMapped = nullptr; +}; + +class RutabagaVirtGpuResource : public std::enable_shared_from_this<RutabagaVirtGpuResource>, public VirtGpuBlob { + public: + ~RutabagaVirtGpuResource(); + + VirtGpuBlobMappingPtr createMapping(void) override; + + uint32_t getResourceHandle() const override; + uint32_t getBlobHandle() const override; + + int exportBlob(VirtGpuExternalHandle& handle) override; + int wait() override; + + int transferFromHost(uint32_t offset, uint32_t size) override; + int transferToHost(uint32_t offset, uint32_t size) override; + + private: + friend class RutabagaVirtGpuDevice; + + enum class ResourceType { + kBlob, + kPipe, + }; + + RutabagaVirtGpuResource(uint32_t resourceId, + ResourceType resourceType, + uint32_t contextId); + + + const uint32_t mContextId; + const uint32_t mResourceId; + const ResourceType mResourceType; +}; + +class RutabagaVirtGpuDevice : public std::enable_shared_from_this<RutabagaVirtGpuDevice>, public VirtGpuDevice { + public: + RutabagaVirtGpuDevice(uint32_t contextId, VirtGpuCapset capset); + ~RutabagaVirtGpuDevice(); + + int64_t getDeviceHandle() override; + + VirtGpuCaps getCaps() override; + + VirtGpuBlobPtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override; + + VirtGpuBlobPtr createVirglBlob(uint32_t width, uint32_t height, uint32_t virglFormat) override; + + VirtGpuBlobPtr importBlob(const struct VirtGpuExternalHandle& handle) override; + + int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuBlob* blob) override; + + private: + const uint32_t mContextId; + const VirtGpuCapset mCapset; + + friend class RutabagaVirtGpuResource; + uint32_t GetContextId() const { return mContextId; } +}; + +} // namespace gfxstream diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp new file mode 100644 index 00000000000..0b7db44d883 --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2023 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 <log/log.h> + +#include "RutabagaLayer.h" +#include "RutabagaVirtGpu.h" + +namespace gfxstream { + +RutabagaVirtGpuResource::RutabagaVirtGpuResource(uint32_t resourceId, ResourceType resourceType, + uint32_t contextId) + : mContextId(contextId), mResourceId(resourceId), mResourceType(resourceType) {} + +RutabagaVirtGpuResource::~RutabagaVirtGpuResource() { + EmulatedVirtioGpu::Get().DestroyResource(mContextId, mResourceId); +} + +VirtGpuBlobMappingPtr RutabagaVirtGpuResource::createMapping(void) { + uint8_t* mapped = EmulatedVirtioGpu::Get().Map(mResourceId); + return std::make_shared<RutabagaVirtGpuBlobMapping>(shared_from_this(), mapped); +} + +uint32_t RutabagaVirtGpuResource::getResourceHandle() const { return mResourceId; } + +uint32_t RutabagaVirtGpuResource::getBlobHandle() const { + if (mResourceType != ResourceType::kBlob) { + ALOGE("Attempting to get blob handle for non-blob resource"); + return -1; + } + + ALOGE("Unimplemented: %s", __FUNCTION__); + return -1; +} + +int RutabagaVirtGpuResource::exportBlob(VirtGpuExternalHandle&) { + if (mResourceType != ResourceType::kBlob) { + ALOGE("Attempting to export blob for non-blob resource"); + return -1; + } + + ALOGE("Unimplemented: %s", __FUNCTION__); + return -1; +} + +int RutabagaVirtGpuResource::wait() { return EmulatedVirtioGpu::Get().Wait(mResourceId); } + +int RutabagaVirtGpuResource::transferFromHost(uint32_t offset, uint32_t size) { + if (mResourceType != ResourceType::kPipe) { + ALOGE("Unexpected transferFromHost() called on non-pipe resource."); + return -1; + } + + return EmulatedVirtioGpu::Get().TransferFromHost(mContextId, mResourceId, offset, size); +} + +int RutabagaVirtGpuResource::transferToHost(uint32_t offset, uint32_t size) { + if (mResourceType != ResourceType::kPipe) { + ALOGE("Unexpected transferToHost() called on non-pipe resource."); + return -1; + } + + return EmulatedVirtioGpu::Get().TransferToHost(mContextId, mResourceId, offset, size); +} + +} // namespace gfxstream diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp new file mode 100644 index 00000000000..4f82f7c4932 --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2023 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 "RutabagaLayer.h" +#include "RutabagaVirtGpu.h" + +namespace gfxstream { + +RutabagaVirtGpuBlobMapping::RutabagaVirtGpuBlobMapping(VirtGpuBlobPtr blob, uint8_t* mapped) + : mBlob(blob), mMapped(mapped) {} + +RutabagaVirtGpuBlobMapping::~RutabagaVirtGpuBlobMapping(void) { + EmulatedVirtioGpu::Get().Unmap(mBlob->getResourceHandle()); +} + +uint8_t* RutabagaVirtGpuBlobMapping::asRawPtr(void) { return mMapped; } + +} // namespace gfxstream diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp new file mode 100644 index 00000000000..999ac2ff58b --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2023 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 <log/log.h> + +#include "RutabagaLayer.h" +#include "RutabagaVirtGpu.h" + +namespace gfxstream { + +RutabagaVirtGpuDevice::RutabagaVirtGpuDevice(uint32_t contextId, VirtGpuCapset capset) + : VirtGpuDevice(capset), mContextId(contextId), mCapset(capset) {} + +RutabagaVirtGpuDevice::~RutabagaVirtGpuDevice() { + EmulatedVirtioGpu::Get().DestroyContext(mContextId); + EmulatedVirtioGpu::Reset(); +} + +int64_t RutabagaVirtGpuDevice::getDeviceHandle() { return -1; } + +VirtGpuCaps RutabagaVirtGpuDevice::getCaps() { return EmulatedVirtioGpu::Get().GetCaps(mCapset); } + +VirtGpuBlobPtr RutabagaVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) { + const auto resourceIdOpt = EmulatedVirtioGpu::Get().CreateBlob(mContextId, blobCreate); + if (!resourceIdOpt) { + return nullptr; + } + + return VirtGpuBlobPtr(new RutabagaVirtGpuResource( + *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kBlob, mContextId)); +} + +VirtGpuBlobPtr RutabagaVirtGpuDevice::createVirglBlob(uint32_t width, uint32_t height, + uint32_t virglFormat) { + const auto resourceIdOpt = + EmulatedVirtioGpu::Get().CreateVirglBlob(mContextId, width, height, virglFormat); + if (!resourceIdOpt) { + return nullptr; + } + + return VirtGpuBlobPtr(new RutabagaVirtGpuResource( + *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kPipe, mContextId)); +} + +int RutabagaVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer, + const VirtGpuBlob* blob) { + std::optional<uint32_t> blobResourceId; + if (blob) { + blobResourceId = blob->getResourceHandle(); + } + return EmulatedVirtioGpu::Get().ExecBuffer(mContextId, execbuffer, blobResourceId); +} + +VirtGpuBlobPtr RutabagaVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle&) { + ALOGE("Unimplemented %s", __FUNCTION__); + return nullptr; +} + +} // namespace gfxstream + +VirtGpuDevice* createPlatformVirtGpuDevice(enum VirtGpuCapset capset, int) { + const auto contextIdOp = gfxstream::EmulatedVirtioGpu::Get().CreateContext(); + if (!contextIdOp) { + ALOGE("Failed to create RutabagaVirtGpuDevice: failed to create context."); + return nullptr; + } + return new gfxstream::RutabagaVirtGpuDevice(*contextIdOp, capset); +} diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp new file mode 100644 index 00000000000..30b5f3d1863 --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2023 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 "RutabagaVirtGpuSyncHelper.h" + +#include "RutabagaLayer.h" + +namespace gfxstream { + +RutabagaVirtGpuSyncHelper::RutabagaVirtGpuSyncHelper() {} + +int RutabagaVirtGpuSyncHelper::wait(int syncFd, int timeoutMilliseconds) { + return EmulatedVirtioGpu::Get().WaitOnEmulatedFence(syncFd, timeoutMilliseconds); +} + +int RutabagaVirtGpuSyncHelper::dup(int syncFd) { + // TODO update reference count + return syncFd; +} + +int RutabagaVirtGpuSyncHelper::close(int) { return 0; } + +SyncHelper* createPlatformSyncHelper() { return new RutabagaVirtGpuSyncHelper(); } + +} // namespace gfxstream
\ No newline at end of file diff --git a/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h new file mode 100644 index 00000000000..c163eb399e9 --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h @@ -0,0 +1,34 @@ +/* + * Copyright 2023 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 "Sync.h" + +namespace gfxstream { + +class RutabagaVirtGpuSyncHelper : public SyncHelper { + public: + RutabagaVirtGpuSyncHelper(); + + int wait(int syncFd, int timeoutMilliseconds) override; + + int dup(int syncFd) override; + + int close(int) override; +}; + +SyncHelper* createPlatformSyncHelper(); + +} // namespace gfxstream diff --git a/src/gfxstream/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h b/src/gfxstream/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h new file mode 100644 index 00000000000..4817a9c2b41 --- /dev/null +++ b/src/gfxstream/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +namespace gfxstream { + +void ResetEmulatedVirtioGpu(); + +} // namespace gfxstream |