Vulkan: Keep track of finished operations with fences

This commit is contained in:
Corentin Wallez 2017-11-22 14:44:21 -05:00 committed by Corentin Wallez
parent 315e9268bb
commit 9018236b38
3 changed files with 103 additions and 7 deletions

View File

@ -103,6 +103,8 @@ namespace vulkan {
ASSERT(memory != nullptr); ASSERT(memory != nullptr);
memcpy(memory + start * sizeof(uint32_t), data, count * sizeof(uint32_t)); memcpy(memory + start * sizeof(uint32_t), data, count * sizeof(uint32_t));
ToBackend(GetDevice())->FakeSubmit();
} }
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) { void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) {
@ -111,6 +113,8 @@ namespace vulkan {
MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker(); MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker();
tracker->Track(this, serial, memory + start); tracker->Track(this, serial, memory + start);
ToBackend(GetDevice())->FakeSubmit();
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {

View File

@ -112,9 +112,23 @@ namespace vulkan {
} }
Device::~Device() { Device::~Device() {
// TODO(cwallez@chromium.org): properly wait on everything to be finished if (fn.QueueWaitIdle(queue) != VK_SUCCESS) {
ASSERT(false);
}
CheckPassedFences();
ASSERT(fencesInFlight.empty());
// Some operations might have been started since the last submit and waiting
// on a serial that doesn't have a corresponding fence enqueued. Force all
// operations to look as if they were completed (because they were).
completedSerial = nextSerial;
Tick(); Tick();
for (VkFence fence : unusedFences) {
fn.DestroyFence(vkDevice, fence, nullptr);
}
unusedFences.clear();
if (memoryAllocator) { if (memoryAllocator) {
delete memoryAllocator; delete memoryAllocator;
memoryAllocator = nullptr; memoryAllocator = nullptr;
@ -207,10 +221,7 @@ namespace vulkan {
} }
void Device::TickImpl() { void Device::TickImpl() {
// TODO(cwallez@chromium.org): Correctly track the serial with Semaphores CheckPassedFences();
fn.QueueWaitIdle(queue);
completedSerial = nextSerial;
nextSerial++;
mapReadRequestTracker->Tick(completedSerial); mapReadRequestTracker->Tick(completedSerial);
memoryAllocator->Tick(completedSerial); memoryAllocator->Tick(completedSerial);
@ -240,6 +251,27 @@ namespace vulkan {
return vkDevice; return vkDevice;
} }
void Device::FakeSubmit() {
VkSubmitInfo submitInfo;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
submitInfo.waitSemaphoreCount = 0;
submitInfo.pWaitSemaphores = nullptr;
submitInfo.pWaitDstStageMask = 0;
submitInfo.commandBufferCount = 0;
submitInfo.pCommandBuffers = 0;
submitInfo.signalSemaphoreCount = 0;
submitInfo.pSignalSemaphores = 0;
VkFence fence = GetUnusedFence();
if (fn.QueueSubmit(queue, 1, &submitInfo, fence) != VK_SUCCESS) {
ASSERT(false);
}
fencesInFlight.emplace(fence, nextSerial);
nextSerial++;
}
bool Device::CreateInstance(VulkanGlobalKnobs* usedKnobs) { bool Device::CreateInstance(VulkanGlobalKnobs* usedKnobs) {
std::vector<const char*> layersToRequest; std::vector<const char*> layersToRequest;
std::vector<const char*> extensionsToRequest; std::vector<const char*> extensionsToRequest;
@ -378,6 +410,52 @@ namespace vulkan {
return const_cast<VulkanFunctions*>(&fn); return const_cast<VulkanFunctions*>(&fn);
} }
VkFence Device::GetUnusedFence() {
if (!unusedFences.empty()) {
VkFence fence = unusedFences.back();
unusedFences.pop_back();
return fence;
}
VkFenceCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
VkFence fence = VK_NULL_HANDLE;
if (fn.CreateFence(vkDevice, &createInfo, nullptr, &fence) != VK_SUCCESS) {
ASSERT(false);
}
return fence;
}
void Device::CheckPassedFences() {
while (!fencesInFlight.empty()) {
VkFence fence = fencesInFlight.front().first;
Serial fenceSerial = fencesInFlight.front().second;
VkResult result = fn.GetFenceStatus(vkDevice, fence);
ASSERT(result == VK_SUCCESS || result == VK_NOT_READY);
// Fence are added in order, so we can stop searching as soon
// as we see one that's not ready.
if (result == VK_NOT_READY) {
return;
}
if (fn.ResetFences(vkDevice, 1, &fence) != VK_SUCCESS) {
ASSERT(false);
}
unusedFences.push_back(fence);
fencesInFlight.pop();
ASSERT(fenceSerial > completedSerial);
completedSerial = fenceSerial;
}
}
// Queue // Queue
Queue::Queue(QueueBuilder* builder) Queue::Queue(QueueBuilder* builder)

View File

@ -40,6 +40,8 @@
#include "common/DynamicLib.h" #include "common/DynamicLib.h"
#include "common/Serial.h" #include "common/Serial.h"
#include <queue>
namespace backend { namespace backend {
namespace vulkan { namespace vulkan {
@ -133,6 +135,8 @@ namespace vulkan {
VkInstance GetInstance() const; VkInstance GetInstance() const;
VkDevice GetVkDevice() const; VkDevice GetVkDevice() const;
void FakeSubmit();
private: private:
bool CreateInstance(VulkanGlobalKnobs* usedKnobs); bool CreateInstance(VulkanGlobalKnobs* usedKnobs);
bool CreateDevice(VulkanDeviceKnobs* usedKnobs); bool CreateDevice(VulkanDeviceKnobs* usedKnobs);
@ -164,10 +168,20 @@ namespace vulkan {
VkQueue queue = VK_NULL_HANDLE; VkQueue queue = VK_NULL_HANDLE;
VkDebugReportCallbackEXT debugReportCallback = VK_NULL_HANDLE; VkDebugReportCallbackEXT debugReportCallback = VK_NULL_HANDLE;
Serial nextSerial = 1;
Serial completedSerial = 0;
MapReadRequestTracker* mapReadRequestTracker = nullptr; MapReadRequestTracker* mapReadRequestTracker = nullptr;
MemoryAllocator* memoryAllocator = nullptr; MemoryAllocator* memoryAllocator = nullptr;
VkFence GetUnusedFence();
void CheckPassedFences();
// We track which operations are in flight on the GPU with an increasing serial.
// This works only because we have a single queue. Each submit to a queue is associated
// to a serial and a fence, such that when the fence is "ready" we know the operations
// have finished.
std::queue<std::pair<VkFence, Serial>> fencesInFlight;
std::vector<VkFence> unusedFences;
Serial nextSerial = 1;
Serial completedSerial = 0;
}; };
class Queue : public QueueBase { class Queue : public QueueBase {