aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYilong Li <liyl@google.com>2021-04-27 11:48:26 -0700
committerYilong Li <liyl@google.com>2021-04-28 18:11:25 +0000
commit53d06dabf94e9b8226b3d402b8cc56dfe5702ee5 (patch)
treeabbd0acda372ee6ff8e44126a152a49242de65b1
parent5dc831b5e8ca40019a45b0b61d6e13b732396243 (diff)
downloadgoldfish-opengl-53d06dabf94e9b8226b3d402b8cc56dfe5702ee5.tar.gz
sync: use fence pool to retrieve temporary VkFences
In the previous change, every time clients call vkQueueSubmit() with external (zircon/syncfd) semaphores to trigger, we create a temporary VkFence as the |fence| argument, let vkQueueSubmit() signal that temporary fence and then do async wait on that fence. In this way, a new VkFence is created and destroyed every queue submit as well as goldfish-sync timelines, which caused some extra overhead. This change introduces a thread-safe SyncFencePool from which clients can acquire a pair of unused VkFence and sync timeline FIDL client, and return them once they finish using them. TEST=FEMU workstation.x64 Change-Id: I281637947360f8c7197d2af132e4d691588628aa
-rw-r--r--BUILD.gn3
-rw-r--r--android-emu/android/base/synchronization/AndroidObjectPool.h132
-rw-r--r--system/vulkan_enc/ResourceTracker.cpp68
-rw-r--r--system/vulkan_enc/SyncFencePool.cpp95
-rw-r--r--system/vulkan_enc/SyncFencePool.h67
5 files changed, 325 insertions, 40 deletions
diff --git a/BUILD.gn b/BUILD.gn
index f2e28f82..a9564fee 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -19,6 +19,7 @@ shared_library("libvulkan_goldfish") {
"android-emu/android/base/synchronization/AndroidLock.h",
"android-emu/android/base/synchronization/AndroidMessageChannel.cpp",
"android-emu/android/base/synchronization/AndroidMessageChannel.h",
+ "android-emu/android/base/synchronization/AndroidObjectPool.h",
"android-emu/android/base/threads/AndroidFunctorThread.cpp",
"android-emu/android/base/threads/AndroidFunctorThread.h",
"android-emu/android/base/threads/AndroidThread.h",
@@ -63,6 +64,8 @@ shared_library("libvulkan_goldfish") {
"system/vulkan_enc/ResourceTracker.h",
"system/vulkan_enc/Resources.cpp",
"system/vulkan_enc/Resources.h",
+ "system/vulkan_enc/SyncFencePool.cpp",
+ "system/vulkan_enc/SyncFencePool.h",
"system/vulkan_enc/Validation.cpp",
"system/vulkan_enc/Validation.h",
"system/vulkan_enc/VkEncoder.cpp",
diff --git a/android-emu/android/base/synchronization/AndroidObjectPool.h b/android-emu/android/base/synchronization/AndroidObjectPool.h
new file mode 100644
index 00000000..45eb499b
--- /dev/null
+++ b/android-emu/android/base/synchronization/AndroidObjectPool.h
@@ -0,0 +1,132 @@
+// Copyright 2021 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 "android/base/synchronization/AndroidConditionVariable.h"
+#include "android/base/synchronization/AndroidLock.h"
+
+#include <list>
+#include <queue>
+
+namespace android {
+namespace base {
+namespace guest {
+
+template <class T>
+class ObjectPool {
+public:
+ explicit ObjectPool(size_t sizeLimit,
+ std::function<T()> createObject,
+ std::function<void(T&)> onDestroy = {[] {}},
+ std::function<void(T&)> onRelease = {[] {}})
+ : mCreateObject(std::move(createObject)),
+ mOnDestroy(std::move(onDestroy)),
+ mOnRelease(std::move(onRelease)),
+ mSizeLimit(sizeLimit) {}
+
+ virtual ~ObjectPool() {
+ while (!mObjects.empty()) {
+ mOnDestroy(mObjects.back());
+ mObjects.pop_back();
+ }
+ };
+
+ void setCreateObjectFunc(std::function<T()> createObject) {
+ mCreateObject = std::move(createObject);
+ }
+
+ void setObjectOnDestroyCallback(std::function<void(T&)> onDestroy) {
+ mOnDestroy = std::move(onDestroy);
+ }
+
+ void setObjectOnReleaseCallback(std::function<void(T&)> onRelease) {
+ mOnRelease = std::move(onRelease);
+ }
+
+ T* acquire() {
+ AutoLock lock(mLock);
+ T* obj = nullptr;
+ if (!mAvailableObjects.empty()) {
+ obj = mAvailableObjects.front();
+ mAvailableObjects.pop();
+ return obj;
+ }
+
+ if (mSizeLimit == 0 || mObjects.size() < mSizeLimit) {
+ mObjects.emplace_back(mCreateObject());
+ return &mObjects.back();
+ }
+
+ mCv.wait(&lock, [this, &obj] {
+ if (!mAvailableObjects.empty()) {
+ obj = mAvailableObjects.front();
+ mAvailableObjects.pop();
+ return true;
+ }
+ return false;
+ });
+ return obj;
+ }
+
+ void release(T* obj) {
+ mOnRelease(*obj);
+
+ AutoLock lock(mLock);
+ mAvailableObjects.push(obj);
+ mCv.signalAndUnlock(&lock);
+ }
+
+private:
+ // Called when ObjectPool creates a new Object.
+ std::function<T()> mCreateObject;
+
+ // Called when an Object is destroyed.
+ // This function will be called in the base class destructor, thus any
+ // method / member variables from derived class should NOT be used in
+ // this function.
+ std::function<void(T& obj)> mOnDestroy;
+
+ // Called when an Object is released back to the pool.
+ std::function<void(T& obj)> mOnRelease;
+
+ android::base::guest::StaticLock mLock;
+ android::base::guest::StaticLock mCvLock;
+ android::base::guest::ConditionVariable mCv;
+ std::queue<T*> mAvailableObjects;
+ std::list<T> mObjects;
+ size_t mTotalObjectsCreated = 0u;
+
+ size_t mSizeLimit = 1u;
+};
+
+template <class T>
+class DefaultObjectPool : public ObjectPool<T> {
+public:
+ explicit DefaultObjectPool(size_t sizeLimit)
+ : ObjectPool<T>(
+ sizeLimit,
+ /* createObject */ [] { return T{}; },
+ /* onDestroy */ [] {},
+ /* onRelease */ [] {}) {}
+ ~DefaultObjectPool() = default;
+
+private:
+ T createObject() { return T{}; }
+ void destroyObject(T&) {}
+};
+
+} // namespace guest
+} // namespace base
+} // namespace android
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index 1dfc5f1e..3b6efe85 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -125,6 +125,7 @@ VkResult getMemoryAndroidHardwareBufferANDROID(struct AHardwareBuffer **) { retu
#include "HostVisibleMemoryVirtualization.h"
#include "Resources.h"
+#include "SyncFencePool.h"
#include "VkEncoder.h"
#include "android/base/AlignedBuf.h"
@@ -315,6 +316,7 @@ public:
std::vector<HostMemBlocks> hostMemBlocks { VK_MAX_MEMORY_TYPES };
uint32_t apiVersion;
std::set<std::string> enabledExtensions;
+ std::unique_ptr<SyncFencePool> fencePool;
};
struct VirtioGpuHostmemResourceInfo {
@@ -345,7 +347,6 @@ public:
uint64_t id;
VkFence fence = VK_NULL_HANDLE;
- VkFence tempFence = VK_NULL_HANDLE;
#ifdef VK_USE_PLATFORM_FUCHSIA
std::vector<zx::event> eventsToWait;
@@ -353,7 +354,6 @@ public:
zx::eventpair syncEvent;
std::unique_ptr<async::WaitOnce> wait;
- fidl::WireSyncClient<fuchsia_hardware_goldfish::SyncTimeline> timelineClient;
#endif // VK_USE_PLATFORM_FUCHSIA
};
@@ -491,7 +491,6 @@ public:
auto it = info_VkDevice.find(device);
if (it == info_VkDevice.end()) return;
- auto info = it->second;
info_VkDevice.erase(device);
lock.unlock();
}
@@ -845,6 +844,9 @@ public:
mFeatureInfo.get(),
&mHostVisibleMemoryVirtInfo);
info.apiVersion = props.apiVersion;
+#ifdef VK_USE_PLATFORM_FUCHSIA
+ info.fencePool = std::make_unique<SyncFencePool>(device, mSyncDevice.get());
+#endif // VK_USE_PLATFORM_FUCHSIA
if (!ppEnabledExtensionNames) return;
@@ -1697,7 +1699,7 @@ public:
auto it = info_VkDevice.find(device);
if (it == info_VkDevice.end()) return;
- auto info = it->second;
+ auto hostMemBlocks = it->second.hostMemBlocks;
lock.unlock();
@@ -1706,7 +1708,7 @@ public:
bool freeMemorySyncSupported =
mFeatureInfo->hasVulkanFreeMemorySync;
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) {
- for (auto& block : info.hostMemBlocks[i]) {
+ for (auto& block : hostMemBlocks[i]) {
destroyHostMemAlloc(
freeMemorySyncSupported,
enc, device, &block);
@@ -5722,9 +5724,13 @@ public:
}
lock.unlock();
- // Create a temporary VkFence.
+ // Acquire a VkFence and timeline from syncFencePool.
VkFence tempFence = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
+
+ SyncFencePool* syncFencePool = nullptr;
+ SyncFence* syncFence = nullptr;
+
if (!postWaitEvents.empty()) {
lock.lock();
const auto& queueInfo = info_VkQueue.find(queue);
@@ -5732,19 +5738,15 @@ public:
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
device = queueInfo->second.device;
+ const auto& deviceInfo = info_VkDevice.find(device);
+ if (deviceInfo == info_VkDevice.end() || deviceInfo->second.fencePool == nullptr) {
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
+ syncFencePool = deviceInfo->second.fencePool.get();
lock.unlock();
- VkFenceCreateInfo fenceCreateInfo = {
- .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- };
-
- auto result = enc->vkCreateFence(device, &fenceCreateInfo, nullptr, &tempFence,
- true /* do lock */);
- if (result != VK_SUCCESS) {
- return result;
- }
+ syncFence = syncFencePool->acquire();
+ tempFence = syncFence->fence;
}
if (!preSignalSemaphores.empty()) {
@@ -5790,20 +5792,6 @@ public:
}
if (!postWaitEvents.empty()) {
- auto timelineEnds = fidl::CreateEndpoints<fuchsia_hardware_goldfish::SyncTimeline>();
- if (!timelineEnds.is_ok()) {
- ALOGE("Cannot create sync timeline channels, error: %s",
- timelineEnds.status_string());
- return VK_ERROR_DEVICE_LOST;
- }
-
- auto createTimelineResult =
- mSyncDevice->CreateTimeline(std::move(timelineEnds->server));
- if (!createTimelineResult.ok()) {
- ALOGE("CreateTimeline failed, error: %s", createTimelineResult.status_string());
- return VK_ERROR_DEVICE_LOST;
- }
-
zx::eventpair syncEventClient, syncEventServer;
auto status = zx::eventpair::create(0u, &syncEventClient, &syncEventServer);
if (status != ZX_OK) {
@@ -5818,10 +5806,7 @@ public:
queueSubmitState = {
.id = queueInfo.vkQueueSubmitId,
.fence = fence,
- .tempFence = tempFence,
.syncEvent = std::move(syncEventClient),
- .timelineClient =
- fidl::WireSyncClient(fidl::BindSyncClient(std::move(timelineEnds->client))),
};
queueSubmitState.wait = std::make_unique<async::WaitOnce>(
@@ -5829,7 +5814,7 @@ public:
queueSubmitState.wait->Begin(
mLoop->dispatcher(),
[this, queue, id = queueSubmitState.id, postWaitEvents /* copy of zx handles */,
- fence, device, tempFence](async_dispatcher_t* dispatcher, async::WaitOnce* wait,
+ fence, device, syncFence](async_dispatcher_t* dispatcher, async::WaitOnce* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
#ifndef FUCHSIA_NO_TRACE
AEMU_SCOPED_TRACE("sync event handler");
@@ -5860,13 +5845,16 @@ public:
break;
}
- vkEncoder->vkDestroyFence(device, tempFence, nullptr, true /* do lock */);
-
- AutoLock lock(mLock);
- info_VkQueue[queue].vkQueueSubmitStateMap.erase(id);
+ SyncFencePool* fencePool = nullptr;
+ {
+ AutoLock lock(mLock);
+ info_VkQueue[queue].vkQueueSubmitStateMap.erase(id);
+ fencePool = info_VkDevice[device].fencePool.get();
+ }
+ fencePool->release(syncFence);
});
- auto triggerResult = queueSubmitState.timelineClient.TriggerHostWait(
+ auto triggerResult = syncFence->timelineClient.TriggerHostWait(
get_host_u64_VkFence(tempFence), 1u, std::move(syncEventServer));
if (!triggerResult.ok()) {
ALOGE("TriggerHostWait failed, error: %s", triggerResult.status_string());
diff --git a/system/vulkan_enc/SyncFencePool.cpp b/system/vulkan_enc/SyncFencePool.cpp
new file mode 100644
index 00000000..03238828
--- /dev/null
+++ b/system/vulkan_enc/SyncFencePool.cpp
@@ -0,0 +1,95 @@
+// Copyright (C) 2021 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 "SyncFencePool.h"
+#include "VkEncoder.h"
+
+#include <log/log.h>
+#include <vulkan/vulkan.h>
+
+namespace goldfish_vk {
+
+namespace {
+
+static VkFence newFence(VkDevice device) {
+ auto hostConn = ResourceTracker::threadingCallbacks.hostConnectionGetFunc();
+ auto vkEncoder = ResourceTracker::threadingCallbacks.vkEncoderGetFunc(hostConn);
+ VkFenceCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0u,
+ };
+
+ VkFence fence = VK_NULL_HANDLE;
+ VkResult result = vkEncoder->vkCreateFence(device, &createInfo, 0u, &fence, true /* do lock */);
+ if (result != VK_SUCCESS) {
+ ALOGE("%s: vkCreateFence failed: result: %d", result);
+ return VK_NULL_HANDLE;
+ }
+ return fence;
+}
+
+static void destroyFence(VkDevice device, VkFence fence) {
+ auto hostConn = ResourceTracker::threadingCallbacks.hostConnectionGetFunc();
+ auto vkEncoder = ResourceTracker::threadingCallbacks.vkEncoderGetFunc(hostConn);
+
+ vkEncoder->vkDestroyFence(device, fence, nullptr, true /* do lock */);
+}
+
+static void resetFence(VkDevice device, VkFence fence) {
+ auto hostConn = ResourceTracker::threadingCallbacks.hostConnectionGetFunc();
+ auto vkEncoder = ResourceTracker::threadingCallbacks.vkEncoderGetFunc(hostConn);
+
+ vkEncoder->vkResetFences(device, 1, &fence, true /* do lock */);
+}
+
+static SyncTimelineClient newTimelineClient(SyncDeviceClient* syncDevice) {
+#ifdef VK_USE_PLATFORM_FUCHSIA
+ auto timelineEnds = fidl::CreateEndpoints<fuchsia_hardware_goldfish::SyncTimeline>();
+ if (!timelineEnds.is_ok()) {
+ ALOGE("Cannot create sync timeline channels, error: %s", timelineEnds.status_string());
+ return {};
+ }
+
+ auto createTimelineResult = syncDevice->CreateTimeline(std::move(timelineEnds->server));
+ if (!createTimelineResult.ok()) {
+ ALOGE("CreateTimeline failed, error: %s", createTimelineResult.status_string());
+ return {};
+ }
+
+ return fidl::BindSyncClient(std::move(timelineEnds->client));
+#else // !VK_USE_PLATFORM_FUCHSIA
+ (void)syncDevice;
+ return 0;
+#endif // VK_USE_PLATFORM_FUCHSIA
+}
+
+} // namespace
+
+SyncFencePool::SyncFencePool(VkDevice device, SyncDeviceClient* syncDevice)
+ : ObjectPool<SyncFence>(
+ kPoolSizeLimit,
+ // createObject
+ [device, syncDevice] {
+ return SyncFence{
+ .fence = newFence(device),
+ .timelineClient = newTimelineClient(syncDevice),
+ };
+ },
+ // onDestroy
+ [device = device](SyncFence& fence) { destroyFence(device, fence.fence); },
+ // onRelease
+ [device = device](SyncFence& fence) { resetFence(device, fence.fence); }) {}
+
+} // namespace goldfish_vk
diff --git a/system/vulkan_enc/SyncFencePool.h b/system/vulkan_enc/SyncFencePool.h
new file mode 100644
index 00000000..4ea5b853
--- /dev/null
+++ b/system/vulkan_enc/SyncFencePool.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2021 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.
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+#include <fuchsia/hardware/goldfish/llcpp/fidl.h>
+#endif // VK_USE_PLATFORM_FUCHSIA
+
+#include "ResourceTracker.h"
+#include "VkEncoder.h"
+
+#include "android/base/synchronization/AndroidObjectPool.h"
+
+#include <log/log.h>
+#include <vulkan/vulkan.h>
+
+namespace goldfish_vk {
+
+#ifdef VK_USE_PLATFORM_FUCHSIA
+using SyncDeviceClient = fidl::WireSyncClient<fuchsia_hardware_goldfish::SyncDevice>;
+using SyncTimelineClient = fidl::WireSyncClient<fuchsia_hardware_goldfish::SyncTimeline>;
+
+#else // !VK_USE_PLATFORM_FUCHSIA
+using SyncDeviceClient = int;
+using SyncTimelineClient = int;
+
+#endif // VK_USE_PLATFORM_FUCHSIA
+
+// A SyncFence is a pair of a normal VkFence dedicated for host / guest sync
+// purposes and a goldfish sync timeline associated with that fence.
+//
+// When guest calls vkQueueSubmit() and it needs to wait for the submitted
+// commands to finish, it acquires a SyncFence, calls vkQueueSubmit() with the
+// |fence| variable from |SyncFence|, and triggers a VkFence wait using the
+// |timelineClient| member variable.
+struct SyncFence {
+ VkFence fence;
+#ifdef VK_USE_PLATFORM_FUCHSIA
+ SyncTimelineClient timelineClient;
+#endif // VK_USE_PLATFORM_FUCHSIA
+};
+
+// A SyncFencePool stores multiple SyncFence objects and allows for reuse.
+// Every time when clients need a SyncFence, it calls |acquire()| from
+// SyncFencePool to get an fence, and returns it by calling |release()| after
+// it finishes using the fence.
+class SyncFencePool : public android::base::guest::ObjectPool<SyncFence> {
+public:
+ SyncFencePool(VkDevice device, SyncDeviceClient* syncDevice);
+ ~SyncFencePool() = default;
+
+private:
+ // unlimited
+ constexpr static size_t kPoolSizeLimit = 0u;
+};
+
+} // namespace goldfish_vk