Vulkan: Keep track of finished operations with fences
This commit is contained in:
parent
315e9268bb
commit
9018236b38
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue