[Vulkan v2] Frames and fences
This commit is contained in:
parent
5744e6ba38
commit
001120605b
|
@ -9,7 +9,9 @@
|
||||||
|
|
||||||
#include "xenia/ui/vk/vulkan_context.h"
|
#include "xenia/ui/vk/vulkan_context.h"
|
||||||
|
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/ui/vk/vulkan_immediate_drawer.h"
|
#include "xenia/ui/vk/vulkan_immediate_drawer.h"
|
||||||
|
#include "xenia/ui/vk/vulkan_util.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
@ -21,8 +23,31 @@ VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window)
|
||||||
VulkanContext::~VulkanContext() { Shutdown(); }
|
VulkanContext::~VulkanContext() { Shutdown(); }
|
||||||
|
|
||||||
bool VulkanContext::Initialize() {
|
bool VulkanContext::Initialize() {
|
||||||
|
auto device = GetVulkanProvider()->GetDevice();
|
||||||
|
|
||||||
context_lost_ = false;
|
context_lost_ = false;
|
||||||
|
|
||||||
|
current_frame_ = 1;
|
||||||
|
// No frames have been completed yet.
|
||||||
|
last_completed_frame_ = 0;
|
||||||
|
// Keep in sync with the modulo because why not.
|
||||||
|
current_queue_frame_ = 1;
|
||||||
|
|
||||||
|
// Create fences for synchronization of reuse and destruction of transient
|
||||||
|
// objects (like command buffers) and for global shutdown.
|
||||||
|
VkFenceCreateInfo fence_create_info;
|
||||||
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||||
|
fence_create_info.pNext = nullptr;
|
||||||
|
fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||||
|
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
|
||||||
|
if (vkCreateFence(device, &fence_create_info, nullptr, &fences_[i]) !=
|
||||||
|
VK_SUCCESS) {
|
||||||
|
XELOGE("Failed to create a Vulkan fence");
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (target_window_) {
|
if (target_window_) {
|
||||||
// Initialize the immediate mode drawer if not offscreen.
|
// Initialize the immediate mode drawer if not offscreen.
|
||||||
immediate_drawer_ = std::make_unique<VulkanImmediateDrawer>(this);
|
immediate_drawer_ = std::make_unique<VulkanImmediateDrawer>(this);
|
||||||
|
@ -36,9 +61,19 @@ bool VulkanContext::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::Shutdown() {
|
void VulkanContext::Shutdown() {
|
||||||
|
auto device = GetVulkanProvider()->GetDevice();
|
||||||
|
|
||||||
|
if (initialized_fully_ && !context_lost_) {
|
||||||
|
AwaitAllFramesCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
initialized_fully_ = false;
|
initialized_fully_ = false;
|
||||||
|
|
||||||
immediate_drawer_.reset();
|
immediate_drawer_.reset();
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < kQueuedFrames; ++i) {
|
||||||
|
util::DestroyAndNullHandle(vkDestroyFence, device, fences_[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmediateDrawer* VulkanContext::immediate_drawer() {
|
ImmediateDrawer* VulkanContext::immediate_drawer() {
|
||||||
|
@ -51,15 +86,70 @@ bool VulkanContext::MakeCurrent() { return true; }
|
||||||
|
|
||||||
void VulkanContext::ClearCurrent() {}
|
void VulkanContext::ClearCurrent() {}
|
||||||
|
|
||||||
void VulkanContext::BeginSwap() {}
|
void VulkanContext::BeginSwap() {
|
||||||
|
if (context_lost_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanContext::EndSwap() {}
|
auto device = GetVulkanProvider()->GetDevice();
|
||||||
|
|
||||||
|
// Await the availability of transient objects for the new frame.
|
||||||
|
// The frame number is incremented in EndSwap so it can be treated the same
|
||||||
|
// way both when inside a frame and when outside of it (it's tied to actual
|
||||||
|
// submissions).
|
||||||
|
if (vkWaitForFences(device, 1, &fences_[current_queue_frame_], VK_TRUE,
|
||||||
|
UINT64_MAX) != VK_SUCCESS) {
|
||||||
|
context_lost_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Update the completed frame if didn't explicitly await all queued frames.
|
||||||
|
if (last_completed_frame_ + kQueuedFrames < current_frame_) {
|
||||||
|
last_completed_frame_ = current_frame_ - kQueuedFrames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::EndSwap() {
|
||||||
|
if (context_lost_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the next transient object frame.
|
||||||
|
auto queue = GetVulkanProvider()->GetGraphicsQueue();
|
||||||
|
if (vkQueueSubmit(queue, 0, nullptr, fences_[current_queue_frame_]) !=
|
||||||
|
VK_SUCCESS) {
|
||||||
|
context_lost_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
++current_queue_frame_;
|
||||||
|
if (current_queue_frame_ >= kQueuedFrames) {
|
||||||
|
current_queue_frame_ -= kQueuedFrames;
|
||||||
|
}
|
||||||
|
++current_frame_;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<RawImage> VulkanContext::Capture() {
|
std::unique_ptr<RawImage> VulkanContext::Capture() {
|
||||||
// TODO(Triang3l): Read back swap chain front buffer.
|
// TODO(Triang3l): Read back swap chain front buffer.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanContext::AwaitAllFramesCompletion() {
|
||||||
|
// Await the last frame since previous frames must be completed before it.
|
||||||
|
if (context_lost_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto device = GetVulkanProvider()->GetDevice();
|
||||||
|
uint32_t await_frame = current_queue_frame_ + (kQueuedFrames - 1);
|
||||||
|
if (await_frame >= kQueuedFrames) {
|
||||||
|
await_frame -= kQueuedFrames;
|
||||||
|
}
|
||||||
|
if (vkWaitForFences(device, 1, &fences_[await_frame], VK_TRUE, UINT64_MAX) !=
|
||||||
|
VK_SUCCESS) {
|
||||||
|
context_lost_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_completed_frame_ = current_frame_ - 1;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace vk
|
} // namespace vk
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -39,6 +39,20 @@ class VulkanContext : public GraphicsContext {
|
||||||
|
|
||||||
std::unique_ptr<RawImage> Capture() override;
|
std::unique_ptr<RawImage> Capture() override;
|
||||||
|
|
||||||
|
VulkanProvider* GetVulkanProvider() const {
|
||||||
|
return static_cast<VulkanProvider*>(provider_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The count of copies of transient objects (like command buffers, dynamic
|
||||||
|
// descriptor pools) that must be kept when rendering with this context.
|
||||||
|
static constexpr uint32_t kQueuedFrames = 3;
|
||||||
|
// The current absolute frame number.
|
||||||
|
uint64_t GetCurrentFrame() { return current_frame_; }
|
||||||
|
// The last completed frame - it's fine to destroy objects used in it.
|
||||||
|
uint64_t GetLastCompletedFrame() { return last_completed_frame_; }
|
||||||
|
uint32_t GetCurrentQueueFrame() { return current_queue_frame_; }
|
||||||
|
void AwaitAllFramesCompletion();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class VulkanProvider;
|
friend class VulkanProvider;
|
||||||
|
|
||||||
|
@ -52,6 +66,11 @@ class VulkanContext : public GraphicsContext {
|
||||||
|
|
||||||
bool context_lost_ = false;
|
bool context_lost_ = false;
|
||||||
|
|
||||||
|
uint64_t current_frame_ = 1;
|
||||||
|
uint64_t last_completed_frame_ = 0;
|
||||||
|
uint32_t current_queue_frame_ = 1;
|
||||||
|
VkFence fences_[kQueuedFrames] = {};
|
||||||
|
|
||||||
std::unique_ptr<VulkanImmediateDrawer> immediate_drawer_ = nullptr;
|
std::unique_ptr<VulkanImmediateDrawer> immediate_drawer_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ VulkanProvider::~VulkanProvider() {
|
||||||
|
|
||||||
bool VulkanProvider::Initialize() {
|
bool VulkanProvider::Initialize() {
|
||||||
if (volkInitialize() != VK_SUCCESS) {
|
if (volkInitialize() != VK_SUCCESS) {
|
||||||
XELOGE("Failed to initialize the Vulkan loader volk.");
|
XELOGE("Failed to initialize the Vulkan loader volk");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ bool VulkanProvider::Initialize() {
|
||||||
instance_create_info.ppEnabledExtensionNames = instance_extensions;
|
instance_create_info.ppEnabledExtensionNames = instance_extensions;
|
||||||
if (vkCreateInstance(&instance_create_info, nullptr, &instance_) !=
|
if (vkCreateInstance(&instance_create_info, nullptr, &instance_) !=
|
||||||
VK_SUCCESS) {
|
VK_SUCCESS) {
|
||||||
XELOGE("Failed to create a Vulkan instance.");
|
XELOGE("Failed to create a Vulkan instance");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
volkLoadInstance(instance_);
|
volkLoadInstance(instance_);
|
||||||
|
@ -112,13 +112,13 @@ bool VulkanProvider::Initialize() {
|
||||||
uint32_t physical_device_count;
|
uint32_t physical_device_count;
|
||||||
if (vkEnumeratePhysicalDevices(instance_, &physical_device_count, nullptr) !=
|
if (vkEnumeratePhysicalDevices(instance_, &physical_device_count, nullptr) !=
|
||||||
VK_SUCCESS) {
|
VK_SUCCESS) {
|
||||||
XELOGE("Failed to get Vulkan physical device count.");
|
XELOGE("Failed to get Vulkan physical device count");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
physical_devices.resize(physical_device_count);
|
physical_devices.resize(physical_device_count);
|
||||||
if (vkEnumeratePhysicalDevices(instance_, &physical_device_count,
|
if (vkEnumeratePhysicalDevices(instance_, &physical_device_count,
|
||||||
physical_devices.data()) != VK_SUCCESS) {
|
physical_devices.data()) != VK_SUCCESS) {
|
||||||
XELOGE("Failed to get Vulkan physical devices.");
|
XELOGE("Failed to get Vulkan physical devices");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
physical_devices.resize(physical_device_count);
|
physical_devices.resize(physical_device_count);
|
||||||
|
@ -215,7 +215,7 @@ bool VulkanProvider::Initialize() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (physical_device_ == VK_NULL_HANDLE) {
|
if (physical_device_ == VK_NULL_HANDLE) {
|
||||||
XELOGE("Failed to get a supported Vulkan physical device.");
|
XELOGE("Failed to get a supported Vulkan physical device");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO(Triang3l): Check if VK_EXT_fragment_shader_interlock and
|
// TODO(Triang3l): Check if VK_EXT_fragment_shader_interlock and
|
||||||
|
@ -254,9 +254,10 @@ bool VulkanProvider::Initialize() {
|
||||||
device_create_info.pEnabledFeatures = nullptr;
|
device_create_info.pEnabledFeatures = nullptr;
|
||||||
if (vkCreateDevice(physical_device_, &device_create_info, nullptr,
|
if (vkCreateDevice(physical_device_, &device_create_info, nullptr,
|
||||||
&device_) != VK_SUCCESS) {
|
&device_) != VK_SUCCESS) {
|
||||||
XELOGE("Failed to create a Vulkan device.");
|
XELOGE("Failed to create a Vulkan device");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
volkLoadDevice(device_);
|
||||||
vkGetDeviceQueue(device_, queue_family, 0, &graphics_queue_);
|
vkGetDeviceQueue(device_, queue_family, 0, &graphics_queue_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -34,6 +34,12 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
Window* target_window) override;
|
Window* target_window) override;
|
||||||
std::unique_ptr<GraphicsContext> CreateOffscreenContext() override;
|
std::unique_ptr<GraphicsContext> CreateOffscreenContext() override;
|
||||||
|
|
||||||
|
const VkPhysicalDeviceFeatures& GetPhysicalDeviceFeatures() const {
|
||||||
|
return physical_device_features_;
|
||||||
|
}
|
||||||
|
VkDevice GetDevice() const { return device_; }
|
||||||
|
VkQueue GetGraphicsQueue() const { return graphics_queue_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit VulkanProvider(Window* main_window);
|
explicit VulkanProvider(Window* main_window);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,17 @@ inline bool DestroyAndNullHandle(F* destroy_function, T& handle) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F, typename T>
|
||||||
|
inline bool DestroyAndNullHandle(F* destroy_function, VkDevice device,
|
||||||
|
T& handle) {
|
||||||
|
if (handle != VK_NULL_HANDLE) {
|
||||||
|
destroy_function(device, handle, nullptr);
|
||||||
|
handle = VK_NULL_HANDLE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace vk
|
} // namespace vk
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|
Loading…
Reference in New Issue