Skip to content

Commit

Permalink
vk: optimize headless swapchain (google#7264)
Browse files Browse the repository at this point in the history
 - Remove queue submit call when using headless swapchain. It was meant
    to emulate a real swapchain, but queue submits are expensive.
 - Add option to remove flush and wait when window resizes. If a
    headless platform uses this signal to refresh the swapchain, we
    don't necessarily need it to also flush and wait before the refresh.
 - Refactor VulkanPlatform customizations
  • Loading branch information
poweifeng authored and plepers committed Dec 9, 2023
1 parent 82d4684 commit 821744d
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 58 deletions.
51 changes: 30 additions & 21 deletions filament/backend/include/backend/platforms/VulkanPlatform.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <backend/Platform.h>

#include <bluevk/BlueVK.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/PrivateImplementation.h>

Expand Down Expand Up @@ -89,36 +90,44 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation<VulkanPlatf

// ----------------------------------------------------
// ---------- Platform Customization options ----------
/**
* The client preference can be stored within the struct. We allow for two specification of
* preference:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`.
* 2) Index of the device in the list as returned by vkEnumeratePhysicalDevices.
*/
struct GPUPreference {
std::string deviceName;
int8_t index = -1;
struct Customization {
/**
* The client can specify the GPU (i.e. VkDevice) for the platform. We allow the
* following preferences:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`. Empty string
* by default.
* 2) Index of the device in the list as returned by
* `vkEnumeratePhysicalDevices`. -1 by default to indicate no preference.
*/
struct GPUPreference {
utils::CString deviceName;
int8_t index = -1;
} gpu;

/**
* Whether the platform supports sRGB swapchain. Default is true.
*/
bool isSRGBSwapChainSupported = true;

/**
* When the platform window is resized, we will flush and wait on the command queues
* before recreating the swapchain. Default is true.
*/
bool flushAndWaitOnWindowResize = true;
};

/**
* Client can provide a preference over the GPU to use in the vulkan instance
* @return `GPUPreference` struct that indicates the client's preference
* Client can override to indicate customized behavior or parameter for their platform.
* @return `Customization` struct that indicates the client's platform
* customizations.
*/
virtual GPUPreference getPreferredGPU() noexcept {
virtual Customization getCustomization() const noexcept {
return {};
}

// -------- End platform customization options --------
// ----------------------------------------------------

/**
* Returns whether the platform supports sRGB swapchain. This is true by default, and the client
* needs to override this method to specify otherwise.
* @return Whether the platform supports sRGB swapchain.
*/
virtual bool isSRGBSwapChainSupported() const {
return true;
}

/**
* Get the images handles and format of the memory backing the swapchain. This should be called
* after createSwapChain() or after recreateIfResized().
Expand Down
5 changes: 3 additions & 2 deletions filament/backend/src/vulkan/VulkanDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mThreadSafeResourceManager(&mResourceAllocator),
mPipelineCache(&mResourceAllocator),
mBlitter(mStagePool, mPipelineCache, mFramebufferCache, mSamplerCache),
mReadPixels(mPlatform->getDevice()) {
mReadPixels(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {

#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback
Expand Down Expand Up @@ -757,7 +758,7 @@ bool VulkanDriver::isAutoDepthResolveSupported() {
}

bool VulkanDriver::isSRGBSwapChainSupported() {
return mPlatform->isSRGBSwapChainSupported();
return mIsSRGBSwapChainSupported;
}

bool VulkanDriver::isStereoSupported() {
Expand Down
2 changes: 2 additions & 0 deletions filament/backend/src/vulkan/VulkanDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class VulkanDriver final : public DriverBase {
VulkanBlitter mBlitter;
VulkanSamplerGroup* mSamplerBindings[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
VulkanReadPixels mReadPixels;

bool const mIsSRGBSwapChainSupported;
};

} // namespace filament::backend
Expand Down
25 changes: 19 additions & 6 deletions filament/backend/src/vulkan/VulkanSwapChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
mAllocator(allocator),
mStagePool(stagePool),
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
mImageReady(VK_NULL_HANDLE),
mAcquired(false),
mIsFirstRenderPass(true) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
Expand All @@ -42,8 +44,13 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
VkSemaphoreCreateInfo const createInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
VkResult result = vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, &mImageReady);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");

// No need to wait on this semaphore before drawing when in Headless mode.
if (!mHeadless) {
VkResult result =
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, &mImageReady);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
}

update();
}
Expand All @@ -55,7 +62,9 @@ VulkanSwapChain::~VulkanSwapChain() {
mCommands->wait();

mPlatform->destroy(swapChain);
vkDestroySemaphore(mPlatform->getDevice(), mImageReady, VKALLOC);
if (mImageReady != VK_NULL_HANDLE) {
vkDestroySemaphore(mPlatform->getDevice(), mImageReady, VKALLOC);
}
}

void VulkanSwapChain::update() {
Expand Down Expand Up @@ -109,16 +118,20 @@ void VulkanSwapChain::acquire(bool& resized) {

// Check if the swapchain should be resized.
if ((resized = mPlatform->hasResized(swapChain))) {
mCommands->flush();
mCommands->wait();
if (mFlushAndWaitOnResize) {
mCommands->flush();
mCommands->wait();
}
mPlatform->recreate(swapChain);
update();
}

VkResult const result = mPlatform->acquire(swapChain, mImageReady, &mCurrentSwapIndex);
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR,
"Cannot acquire in swapchain.");
mCommands->injectDependency(mImageReady);
if (mImageReady != VK_NULL_HANDLE) {
mCommands->injectDependency(mImageReady);
}
mAcquired = true;
}

Expand Down
1 change: 1 addition & 0 deletions filament/backend/src/vulkan/VulkanSwapChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
VmaAllocator mAllocator;
VulkanStagePool& mStagePool;
bool const mHeadless;
bool const mFlushAndWaitOnResize;

// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
// transitions, which are useful here.
Expand Down
12 changes: 6 additions & 6 deletions filament/backend/src/vulkan/platform/VulkanPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,9 @@ inline int deviceTypeOrder(VkPhysicalDeviceType deviceType) {
}

VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
VulkanPlatform::GPUPreference const& gpuPreference) {
FixedCapacityVector<VkPhysicalDevice> const physicalDevices
= filament::backend::enumerate(vkEnumeratePhysicalDevices, instance);
VulkanPlatform::Customization::GPUPreference const& gpuPreference) {
FixedCapacityVector<VkPhysicalDevice> const physicalDevices =
filament::backend::enumerate(vkEnumeratePhysicalDevices, instance);
struct DeviceInfo {
VkPhysicalDevice device = VK_NULL_HANDLE;
VkPhysicalDeviceType deviceType = VK_PHYSICAL_DEVICE_TYPE_OTHER;
Expand Down Expand Up @@ -488,10 +488,10 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
return true;
}
if (!pref.deviceName.empty()) {
if (a.name.find(pref.deviceName) != a.name.npos) {
if (a.name.find(pref.deviceName.c_str()) != a.name.npos) {
return false;
}
if (b.name.find(pref.deviceName) != b.name.npos) {
if (b.name.find(pref.deviceName.c_str()) != b.name.npos) {
return true;
}
}
Expand Down Expand Up @@ -615,7 +615,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,

bluevk::bindInstance(mImpl->mInstance);

VulkanPlatform::GPUPreference const pref = getPreferredGPU();
VulkanPlatform::Customization::GPUPreference const pref = getCustomization().gpu;
bool const hasGPUPreference = pref.index >= 0 || !pref.deviceName.empty();
ASSERT_PRECONDITION(!(hasGPUPreference && sharedContext),
"Cannot both share context and indicate GPU preference");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,21 +327,7 @@ VkResult VulkanPlatformHeadlessSwapChain::present(uint32_t index, VkSemaphore fi
VkResult VulkanPlatformHeadlessSwapChain::acquire(VkSemaphore clientSignal, uint32_t* index) {
*index = mCurrentIndex;
mCurrentIndex = (mCurrentIndex + 1) % HEADLESS_SWAPCHAIN_SIZE;
VkSemaphore const localSignal = clientSignal;
VkSubmitInfo const submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 0,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 1u,
.pSignalSemaphores = &localSignal,
};
UTILS_UNUSED_IN_RELEASE VkResult const result
= vkQueueSubmit(mQueue, 1, &submitInfo, VK_NULL_HANDLE);
assert_invariant(result == VK_SUCCESS);
return result;
return VK_SUCCESS;
}

void VulkanPlatformHeadlessSwapChain::destroy() {
Expand Down
23 changes: 15 additions & 8 deletions libs/filamentapp/src/FilamentApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,30 @@ using namespace filament::backend;
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
class FilamentAppVulkanPlatform : public VulkanPlatform {
public:
FilamentAppVulkanPlatform(std::string const& gpuHint) {
FilamentAppVulkanPlatform(char const* gpuHintCstr) {
utils::CString gpuHint{ gpuHintCstr };
if (gpuHint.empty()) {
return;
}
VulkanPlatform::Customization::GPUPreference pref;
// Check to see if it is an integer, if so turn it into an index.
if (std::all_of(gpuHint.begin(), gpuHint.end(), ::isdigit)) {
mPreference.index = static_cast<int8_t>(std::stoi(gpuHint));
return;
char* p_end {};
pref.index = static_cast<int8_t>(std::strtol(gpuHint.c_str(), &p_end, 10));
} else {
pref.deviceName = gpuHint;
}
mPreference.deviceName = gpuHint;
mCustomization = {
.gpu = pref
};
}

virtual VulkanPlatform::GPUPreference getPreferredGPU() noexcept override {
return mPreference;
virtual VulkanPlatform::Customization getCustomization() const noexcept override {
return mCustomization;
}

private:
VulkanPlatform::GPUPreference mPreference;
VulkanPlatform::Customization mCustomization;
};
#endif

Expand Down Expand Up @@ -592,7 +598,8 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,

if (backend == Engine::Backend::VULKAN) {
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
mFilamentApp->mVulkanPlatform = new FilamentAppVulkanPlatform(config.vulkanGPUHint);
mFilamentApp->mVulkanPlatform =
new FilamentAppVulkanPlatform(config.vulkanGPUHint.c_str());
return Engine::Builder()
.backend(backend)
.platform(mFilamentApp->mVulkanPlatform)
Expand Down

0 comments on commit 821744d

Please sign in to comment.