Skip to content

Commit e4b81b8

Browse files
committed
GPU Vulkan Display: Switch to timeline semaphore (Vulkan 1.2)
1 parent 7394fc2 commit e4b81b8

2 files changed

Lines changed: 63 additions & 38 deletions

File tree

‎GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx‎

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,42 @@ vk::CommandBuffer GPUDisplayBackendVulkan::getSingleTimeCommandBuffer()
163163
return commandBuffer;
164164
}
165165

166+
template <class... Args>
167+
uint64_t GPUDisplayBackendVulkan::queueSubmit(uint64_t* timelineSignalValue, Args&&... args)
168+
{
169+
std::lock_guard<std::mutex> guard(mQueueMutex);
170+
if (timelineSignalValue) {
171+
*timelineSignalValue = ++mTimelineValue;
172+
}
173+
CHKERR(mGraphicsQueue.submit(std::forward<Args>(args)...));
174+
return mTimelineValue;
175+
}
176+
166177
void GPUDisplayBackendVulkan::submitSingleTimeCommandBuffer(vk::CommandBuffer commandBuffer)
167178
{
168179
commandBuffer.end();
169180
vk::SubmitInfo submitInfo{};
170181
submitInfo.commandBufferCount = 1;
171182
submitInfo.pCommandBuffers = &commandBuffer;
172-
static std::mutex fenceMutex;
173-
{
174-
std::lock_guard<std::mutex> guard(fenceMutex);
175-
CHKERR(mGraphicsQueue.submit(1, &submitInfo, mSingleCommitFence));
176-
CHKERR(mDevice.waitForFences(1, &mSingleCommitFence, true, UINT64_MAX));
177-
CHKERR(mDevice.resetFences(1, &mSingleCommitFence));
178-
}
183+
uint64_t signalValue;
184+
vk::TimelineSemaphoreSubmitInfo timelineInfo{};
185+
timelineInfo.signalSemaphoreValueCount = 1;
186+
timelineInfo.pSignalSemaphoreValues = &signalValue;
187+
submitInfo.pNext = &timelineInfo;
188+
submitInfo.signalSemaphoreCount = 1;
189+
submitInfo.pSignalSemaphores = &mTimelineSemaphore;
190+
waitTimeline(queueSubmit(&signalValue, 1, &submitInfo, VkFence(VK_NULL_HANDLE)));
179191
mDevice.freeCommandBuffers(mCommandPool, 1, &commandBuffer);
180192
}
181193

194+
void GPUDisplayBackendVulkan::waitTimeline(uint64_t value)
195+
{
196+
if (value) {
197+
vk::SemaphoreWaitInfo waitInfo{{}, 1, &mTimelineSemaphore, &value};
198+
CHKERR(mDevice.waitSemaphores(&waitInfo, UINT64_MAX));
199+
}
200+
}
201+
182202
static vk::ImageView createImageViewI(vk::Device device, vk::Image image, vk::Format format, vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor, uint32_t mipLevels = 1)
183203
{
184204
vk::ImageViewCreateInfo viewInfo{};
@@ -477,6 +497,7 @@ void GPUDisplayBackendVulkan::createDevice()
477497
clearFeatures(reqDeviceFeatures);
478498
auto& reqFeatures10 = reqDeviceFeatures.get<vk::PhysicalDeviceFeatures2>().features;
479499
reqFeatures10.geometryShader = reqFeatures10.wideLines = reqFeatures10.largePoints = reqFeatures10.multiDrawIndirect = VK_TRUE;
500+
reqDeviceFeatures.get<vk::PhysicalDeviceVulkan12Features>().timelineSemaphore = VK_TRUE;
480501
reqDeviceFeatures.get<vk::PhysicalDeviceSwapchainMaintenance1FeaturesKHR>().swapchainMaintenance1 = VK_TRUE;
481502
reqDeviceFeatures.get<vk::PhysicalDevicePresentIdFeaturesKHR>().presentId = VK_TRUE;
482503
reqDeviceFeatures.get<vk::PhysicalDevicePresentWaitFeaturesKHR>().presentWait = VK_TRUE;
@@ -583,33 +604,30 @@ void GPUDisplayBackendVulkan::clearCommandBuffers()
583604

584605
// ---------------------------- VULKAN SEMAPHORES AND FENCES ----------------------------
585606

586-
void GPUDisplayBackendVulkan::createSemaphoresAndFences()
607+
void GPUDisplayBackendVulkan::createSemaphores()
587608
{
588609
vk::SemaphoreCreateInfo semaphoreInfo{};
589-
vk::FenceCreateInfo fenceInfo{};
590-
fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled;
591610
mImageAvailableSemaphore.resize(mFramesInFlight);
592611
mPresentSemaphore.resize(mImageCount);
593-
mInFlightFence.resize(mFramesInFlight);
594-
mImageFence.resize(mImageCount);
595612
for (uint32_t i = 0; i < mFramesInFlight; i++) {
596613
mImageAvailableSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
597-
mInFlightFence[i] = mDevice.createFence(fenceInfo, nullptr);
598614
}
599615
for (uint32_t i = 0; i < mImageCount; i++) {
600616
mPresentSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
601-
mImageFence[i] = VkFence(VK_NULL_HANDLE);
602617
}
603-
fenceInfo.flags = {};
604-
mSingleCommitFence = mDevice.createFence(fenceInfo, nullptr);
618+
vk::SemaphoreTypeCreateInfo timelineTypeInfo{vk::SemaphoreType::eTimeline, 0};
619+
semaphoreInfo.pNext = &timelineTypeInfo;
620+
mTimelineSemaphore = mDevice.createSemaphore(semaphoreInfo, nullptr);
621+
mTimelineValue = 0;
622+
mFrameTimelineValue.assign(mFramesInFlight, 0);
623+
mImageTimelineValue.assign(mImageCount, 0);
605624
}
606625

607-
void GPUDisplayBackendVulkan::clearSemaphoresAndFences()
626+
void GPUDisplayBackendVulkan::clearSemaphores()
608627
{
609628
clearVector(mImageAvailableSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
610629
clearVector(mPresentSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
611-
clearVector(mInFlightFence, [&](auto& x) { mDevice.destroyFence(x, nullptr); });
612-
mDevice.destroyFence(mSingleCommitFence, nullptr);
630+
mDevice.destroySemaphore(mTimelineSemaphore, nullptr);
613631
}
614632

615633
// ---------------------------- VULKAN UNIFORM LAYOUTS AND BUFFERS ----------------------------
@@ -802,11 +820,11 @@ void GPUDisplayBackendVulkan::createSwapChain(bool forScreenshot, bool forMixing
802820
if (mCommandInfrastructureCreated) {
803821
clearUniformLayoutsAndBuffers(); // TODO: Check what must actually be cleaned up if frames in flight changes
804822
clearCommandBuffers();
805-
clearSemaphoresAndFences();
823+
clearSemaphores();
806824
}
807825
createUniformLayoutsAndBuffers();
808826
createCommandBuffers();
809-
createSemaphoresAndFences();
827+
createSemaphores();
810828
mCommandInfrastructureCreated = true;
811829
}
812830

@@ -1468,7 +1486,7 @@ void GPUDisplayBackendVulkan::ExitBackendA()
14681486
clearOffscreenBuffers();
14691487
clearSwapChain();
14701488
if (mCommandInfrastructureCreated) {
1471-
clearSemaphoresAndFences();
1489+
clearSemaphores();
14721490
clearCommandBuffers();
14731491
clearUniformLayoutsAndBuffers();
14741492
}
@@ -1539,7 +1557,7 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
15391557

15401558
if (includeMixImage == 0.f) {
15411559
mCurrentFrame = (mCurrentFrame + 1) % mFramesInFlight;
1542-
CHKERR(mDevice.waitForFences(1, &mInFlightFence[mCurrentFrame], true, UINT64_MAX));
1560+
waitTimeline(mFrameTimelineValue[mCurrentFrame]);
15431561
auto getImage = [&]() {
15441562
return mDevice.acquireNextImageKHR(mSwapChain, UINT64_MAX, mImageAvailableSemaphore[mCurrentFrame], VkFence(VK_NULL_HANDLE), &mCurrentImageIndex);
15451563
};
@@ -1558,11 +1576,7 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
15581576
}
15591577
mMustUpdateSwapChain = retVal == vk::Result::eSuboptimalKHR;
15601578
CHKERR(retVal);
1561-
if (mImageFence[mCurrentImageIndex]) {
1562-
CHKERR(mDevice.waitForFences(1, &mImageFence[mCurrentImageIndex], true, UINT64_MAX));
1563-
}
1564-
mImageFence[mCurrentImageIndex] = mInFlightFence[mCurrentFrame];
1565-
CHKERR(mDevice.resetFences(1, &mInFlightFence[mCurrentFrame]));
1579+
waitTimeline(mImageTimelineValue[mCurrentImageIndex]); // Previous frame using this image's resources (command buffers, uniform buffer, ...) finished
15661580

15671581
mHasDrawnText = false;
15681582
mFrameCommandBuffers.clear();
@@ -1608,7 +1622,7 @@ void GPUDisplayBackendVulkan::finishFrame(bool doScreenshot, bool toMixBuffer, f
16081622
if (doScreenshot) {
16091623
submitInfo.commandBufferCount = mFrameCommandBuffers.size();
16101624
submitInfo.pCommandBuffers = mFrameCommandBuffers.data();
1611-
CHKERR(mGraphicsQueue.submit(1, &submitInfo, VkFence(VK_NULL_HANDLE)));
1625+
queueSubmit(nullptr, 1, &submitInfo, VkFence(VK_NULL_HANDLE));
16121626
mDevice.waitIdle();
16131627
if (mDisplay->cfgR().screenshotScaleFactor != 1) {
16141628
readImageToPixels(mDownsampleImages[mCurrentImageIndex].image, vk::ImageLayout::eColorAttachmentOptimal, mScreenshotPixels);
@@ -1623,9 +1637,15 @@ void GPUDisplayBackendVulkan::finishFrame(bool doScreenshot, bool toMixBuffer, f
16231637
}
16241638
submitInfo.commandBufferCount = mFrameCommandBuffers.size();
16251639
submitInfo.pCommandBuffers = mFrameCommandBuffers.data();
1626-
submitInfo.signalSemaphoreCount = 1;
1627-
submitInfo.pSignalSemaphores = &mPresentSemaphore[mCurrentImageIndex];
1628-
CHKERR(mGraphicsQueue.submit(1, &submitInfo, mInFlightFence[mCurrentFrame]));
1640+
const vk::Semaphore signalSemaphores[2] = {mPresentSemaphore[mCurrentImageIndex], mTimelineSemaphore};
1641+
uint64_t signalValues[2] = {0, 0}; // First value ignored for the binary present semaphore
1642+
vk::TimelineSemaphoreSubmitInfo timelineInfo{};
1643+
timelineInfo.signalSemaphoreValueCount = 2;
1644+
timelineInfo.pSignalSemaphoreValues = signalValues;
1645+
submitInfo.pNext = &timelineInfo;
1646+
submitInfo.signalSemaphoreCount = 2;
1647+
submitInfo.pSignalSemaphores = signalSemaphores;
1648+
mFrameTimelineValue[mCurrentFrame] = mImageTimelineValue[mCurrentImageIndex] = queueSubmit(&signalValues[1], 1, &submitInfo, VkFence(VK_NULL_HANDLE));
16291649

16301650
vk::PresentInfoKHR presentInfo{};
16311651
presentInfo.waitSemaphoreCount = 1;

‎GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <vector>
2222
#include <unordered_map>
23+
#include <mutex>
2324
#include <utils/vecpod.h>
2425

2526
namespace o2::gpu
@@ -98,6 +99,9 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
9899
void startFillCommandBuffer(vk::CommandBuffer& commandBuffer, uint32_t imageIndex, bool toMixBuffer = false);
99100
void endFillCommandBuffer(vk::CommandBuffer& commandBuffer);
100101
vk::CommandBuffer getSingleTimeCommandBuffer();
102+
void waitTimeline(uint64_t value); // Wait until the timeline semaphore reached value, 0 = nothing to wait for
103+
template <class... Args>
104+
uint64_t queueSubmit(uint64_t* timelineSignalValue, Args&&... args); // Submit under mQueueMutex; if timelineSignalValue (the entry in pSignalSemaphoreValues) is set, write the next timeline value there first. Returns the current timeline value
101105
void submitSingleTimeCommandBuffer(vk::CommandBuffer commandBuffer);
102106
void readImageToPixels(vk::Image image, vk::ImageLayout layout, std::vector<char>& pixels);
103107
void downsampleToFramebuffer(vk::CommandBuffer& commandBuffer);
@@ -108,7 +112,7 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
108112
void createShaders();
109113
void createTextureSampler();
110114
void createCommandBuffers();
111-
void createSemaphoresAndFences();
115+
void createSemaphores();
112116
void createUniformLayoutsAndBuffers();
113117
void createSwapChain(bool forScreenshot = false, bool forMixing = false);
114118
void createOffscreenBuffers(bool forScreenshot = false, bool forMixing = false);
@@ -117,7 +121,7 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
117121
void clearShaders();
118122
void clearTextureSampler();
119123
void clearCommandBuffers();
120-
void clearSemaphoresAndFences();
124+
void clearSemaphores();
121125
void clearUniformLayoutsAndBuffers();
122126
void clearOffscreenBuffers();
123127
void clearSwapChain();
@@ -180,8 +184,11 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
180184
std::vector<bool> mCommandBufferUpToDate;
181185
std::vector<vk::Semaphore> mImageAvailableSemaphore;
182186
std::vector<vk::Semaphore> mPresentSemaphore;
183-
std::vector<vk::Fence> mInFlightFence;
184-
std::vector<vk::Fence> mImageFence;
187+
vk::Semaphore mTimelineSemaphore; // Signaled with an increasing value by every queue submit, replaces fences
188+
uint64_t mTimelineValue = 0; // Last value used for a submit
189+
std::vector<uint64_t> mFrameTimelineValue; // Per frame slot: value of its last submit, slot (and its acquire semaphore) free once reached
190+
std::vector<uint64_t> mImageTimelineValue; // Per swapchain image: value of the last submit using its resources
191+
std::mutex mQueueMutex; // Queue submissions must be externally synchronized
185192
std::vector<VulkanBuffer> mUniformBuffersMat[3];
186193
std::vector<VulkanBuffer> mUniformBuffersCol[3];
187194
std::vector<vk::DescriptorSet> mDescriptorSets[3];
@@ -210,8 +217,6 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend
210217
bool mMixingSupported = 0;
211218

212219
VulkanBuffer mMixingTextureVertexArray;
213-
214-
vk::Fence mSingleCommitFence;
215220
};
216221
} // namespace o2::gpu
217222

0 commit comments

Comments
 (0)