Support rendering windowless (tested on the Vulkan backend)
This commit is contained in:
parent
8fc71f6f7c
commit
09b3a07e3c
|
@ -191,11 +191,13 @@ X_STATUS Emulator::Setup(
|
||||||
// Initialize emulator fallback exception handling last.
|
// Initialize emulator fallback exception handling last.
|
||||||
ExceptionHandler::Install(Emulator::ExceptionCallbackThunk, this);
|
ExceptionHandler::Install(Emulator::ExceptionCallbackThunk, this);
|
||||||
|
|
||||||
// Finish initializing the display.
|
if (display_window_) {
|
||||||
display_window_->loop()->PostSynchronous([this]() {
|
// Finish initializing the display.
|
||||||
xe::ui::GraphicsContextLock context_lock(display_window_->context());
|
display_window_->loop()->PostSynchronous([this]() {
|
||||||
Profiler::set_window(display_window_);
|
xe::ui::GraphicsContextLock context_lock(display_window_->context());
|
||||||
});
|
Profiler::set_window(display_window_);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,16 +51,21 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
// This must happen on the UI thread.
|
// This must happen on the UI thread.
|
||||||
std::unique_ptr<xe::ui::GraphicsContext> processor_context = nullptr;
|
std::unique_ptr<xe::ui::GraphicsContext> processor_context = nullptr;
|
||||||
if (provider_) {
|
if (provider_) {
|
||||||
target_window_->loop()->PostSynchronous([&]() {
|
if (target_window_) {
|
||||||
// Create the context used for presentation.
|
target_window_->loop()->PostSynchronous([&]() {
|
||||||
assert_null(target_window->context());
|
// Create the context used for presentation.
|
||||||
target_window_->set_context(provider_->CreateContext(target_window_));
|
assert_null(target_window->context());
|
||||||
|
target_window_->set_context(provider_->CreateContext(target_window_));
|
||||||
|
|
||||||
// Setup the context the command processor will do all its drawing in.
|
// Setup the context the command processor will do all its drawing in.
|
||||||
// It's shared with the display context so that we can resolve
|
// It's shared with the display context so that we can resolve
|
||||||
// framebuffers from it.
|
// framebuffers from it.
|
||||||
|
processor_context = provider()->CreateOffscreenContext();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
processor_context = provider()->CreateOffscreenContext();
|
processor_context = provider()->CreateOffscreenContext();
|
||||||
});
|
}
|
||||||
|
|
||||||
if (!processor_context) {
|
if (!processor_context) {
|
||||||
xe::FatalError(
|
xe::FatalError(
|
||||||
"Unable to initialize graphics context. Xenia requires OpenGL 4.5 or "
|
"Unable to initialize graphics context. Xenia requires OpenGL 4.5 or "
|
||||||
|
@ -78,16 +83,21 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
XELOGE("Unable to initialize command processor");
|
XELOGE("Unable to initialize command processor");
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
command_processor_->set_swap_request_handler(
|
|
||||||
[this]() { target_window_->Invalidate(); });
|
|
||||||
|
|
||||||
// Watch for paint requests to do our swap.
|
if (target_window) {
|
||||||
target_window->on_painting.AddListener(
|
command_processor_->set_swap_request_handler(
|
||||||
[this](xe::ui::UIEvent* e) { Swap(e); });
|
[this]() { target_window_->Invalidate(); });
|
||||||
|
|
||||||
// Watch for context lost events.
|
// Watch for paint requests to do our swap.
|
||||||
target_window->on_context_lost.AddListener(
|
target_window->on_painting.AddListener(
|
||||||
[this](xe::ui::UIEvent* e) { Reset(); });
|
[this](xe::ui::UIEvent* e) { Swap(e); });
|
||||||
|
|
||||||
|
// Watch for context lost events.
|
||||||
|
target_window->on_context_lost.AddListener(
|
||||||
|
[this](xe::ui::UIEvent* e) { Reset(); });
|
||||||
|
} else {
|
||||||
|
command_processor_->set_swap_request_handler([]() {});
|
||||||
|
}
|
||||||
|
|
||||||
// Let the processor know we want register access callbacks.
|
// Let the processor know we want register access callbacks.
|
||||||
memory_->AddVirtualMappedRange(
|
memory_->AddVirtualMappedRange(
|
||||||
|
|
|
@ -212,13 +212,13 @@ VkResult TextureCache::Initialize() {
|
||||||
invalidated_textures_sets_[1].reserve(64);
|
invalidated_textures_sets_[1].reserve(64);
|
||||||
invalidated_textures_ = &invalidated_textures_sets_[0];
|
invalidated_textures_ = &invalidated_textures_sets_[0];
|
||||||
|
|
||||||
device_queue_ = device_->AcquireQueue();
|
device_queue_ = device_->AcquireQueue(device_->queue_family_index());
|
||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::Shutdown() {
|
void TextureCache::Shutdown() {
|
||||||
if (device_queue_) {
|
if (device_queue_) {
|
||||||
device_->ReleaseQueue(device_queue_);
|
device_->ReleaseQueue(device_queue_, device_->queue_family_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free all textures allocated.
|
// Free all textures allocated.
|
||||||
|
|
|
@ -62,7 +62,7 @@ bool VulkanCommandProcessor::SetupContext() {
|
||||||
// Acquire our device and queue.
|
// Acquire our device and queue.
|
||||||
auto context = static_cast<xe::ui::vulkan::VulkanContext*>(context_.get());
|
auto context = static_cast<xe::ui::vulkan::VulkanContext*>(context_.get());
|
||||||
device_ = context->device();
|
device_ = context->device();
|
||||||
queue_ = device_->AcquireQueue();
|
queue_ = device_->AcquireQueue(device_->queue_family_index());
|
||||||
if (!queue_) {
|
if (!queue_) {
|
||||||
// Need to reuse primary queue (with locks).
|
// Need to reuse primary queue (with locks).
|
||||||
queue_ = device_->primary_queue();
|
queue_ = device_->primary_queue();
|
||||||
|
@ -159,7 +159,7 @@ void VulkanCommandProcessor::ShutdownContext() {
|
||||||
|
|
||||||
// Release queue, if we were using an acquired one.
|
// Release queue, if we were using an acquired one.
|
||||||
if (!queue_mutex_) {
|
if (!queue_mutex_) {
|
||||||
device_->ReleaseQueue(queue_);
|
device_->ReleaseQueue(queue_, device_->queue_family_index());
|
||||||
queue_ = nullptr;
|
queue_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,16 +37,19 @@ X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
kernel::KernelState* kernel_state,
|
kernel::KernelState* kernel_state,
|
||||||
ui::Window* target_window) {
|
ui::Window* target_window) {
|
||||||
// Must create the provider so we can create contexts.
|
// Must create the provider so we can create contexts.
|
||||||
provider_ = xe::ui::vulkan::VulkanProvider::Create(target_window);
|
auto provider = xe::ui::vulkan::VulkanProvider::Create(target_window);
|
||||||
|
device_ = provider->device();
|
||||||
|
provider_ = std::move(provider);
|
||||||
|
|
||||||
auto result = GraphicsSystem::Setup(processor, kernel_state, target_window);
|
auto result = GraphicsSystem::Setup(processor, kernel_state, target_window);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
display_context_ = reinterpret_cast<xe::ui::vulkan::VulkanContext*>(
|
if (target_window) {
|
||||||
target_window->context());
|
display_context_ = reinterpret_cast<xe::ui::vulkan::VulkanContext*>(
|
||||||
device_ = display_context_->device();
|
target_window->context());
|
||||||
|
}
|
||||||
|
|
||||||
// Create our own command pool we can use for captures.
|
// Create our own command pool we can use for captures.
|
||||||
VkCommandPoolCreateInfo create_info = {
|
VkCommandPoolCreateInfo create_info = {
|
||||||
|
|
|
@ -124,12 +124,9 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
|
||||||
uint32_t queue_count = 1;
|
uint32_t queue_count = 1;
|
||||||
for (size_t i = 0; i < device_info.queue_family_properties.size(); ++i) {
|
for (size_t i = 0; i < device_info.queue_family_properties.size(); ++i) {
|
||||||
auto queue_flags = device_info.queue_family_properties[i].queueFlags;
|
auto queue_flags = device_info.queue_family_properties[i].queueFlags;
|
||||||
if (!device_info.queue_family_supports_present[i]) {
|
if (queue_flags & VK_QUEUE_GRAPHICS_BIT &&
|
||||||
// Can't present from this queue, so ignore it.
|
queue_flags & VK_QUEUE_TRANSFER_BIT) {
|
||||||
continue;
|
// Can do graphics and transfer - good!
|
||||||
}
|
|
||||||
if (queue_flags & VK_QUEUE_GRAPHICS_BIT) {
|
|
||||||
// Can do graphics and present - good!
|
|
||||||
ideal_queue_family_index = static_cast<uint32_t>(i);
|
ideal_queue_family_index = static_cast<uint32_t>(i);
|
||||||
// Grab all the queues we can.
|
// Grab all the queues we can.
|
||||||
queue_count = device_info.queue_family_properties[i].queueCount;
|
queue_count = device_info.queue_family_properties[i].queueCount;
|
||||||
|
@ -138,7 +135,7 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
|
||||||
}
|
}
|
||||||
if (ideal_queue_family_index == UINT_MAX) {
|
if (ideal_queue_family_index == UINT_MAX) {
|
||||||
FatalVulkanError(
|
FatalVulkanError(
|
||||||
"No queue families available that can both do graphics and present");
|
"No queue families available that can both do graphics and transfer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,23 +144,36 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
|
||||||
queue_count = 1;
|
queue_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkDeviceQueueCreateInfo queue_info;
|
std::vector<VkDeviceQueueCreateInfo> queue_infos;
|
||||||
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
queue_infos.resize(device_info.queue_family_properties.size());
|
||||||
queue_info.pNext = nullptr;
|
for (int i = 0; i < queue_infos.size(); i++) {
|
||||||
queue_info.flags = 0;
|
VkDeviceQueueCreateInfo& queue_info = queue_infos[i];
|
||||||
queue_info.queueFamilyIndex = ideal_queue_family_index;
|
VkQueueFamilyProperties& family_props =
|
||||||
queue_info.queueCount = queue_count;
|
device_info.queue_family_properties[i];
|
||||||
std::vector<float> queue_priorities(queue_count);
|
|
||||||
// Prioritize the primary queue.
|
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
queue_priorities[0] = 1.0f;
|
queue_info.pNext = nullptr;
|
||||||
queue_info.pQueuePriorities = queue_priorities.data();
|
queue_info.flags = 0;
|
||||||
|
queue_info.queueFamilyIndex = i;
|
||||||
|
queue_info.queueCount = family_props.queueCount;
|
||||||
|
|
||||||
|
std::vector<float> queue_priorities(queue_count);
|
||||||
|
if (i == ideal_queue_family_index) {
|
||||||
|
// Prioritize the first queue on the primary queue family.
|
||||||
|
queue_priorities[0] = 1.0f;
|
||||||
|
} else {
|
||||||
|
queue_priorities[0] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_info.pQueuePriorities = queue_priorities.data();
|
||||||
|
}
|
||||||
|
|
||||||
VkDeviceCreateInfo create_info;
|
VkDeviceCreateInfo create_info;
|
||||||
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
create_info.pNext = nullptr;
|
create_info.pNext = nullptr;
|
||||||
create_info.flags = 0;
|
create_info.flags = 0;
|
||||||
create_info.queueCreateInfoCount = 1;
|
create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size());
|
||||||
create_info.pQueueCreateInfos = &queue_info;
|
create_info.pQueueCreateInfos = queue_infos.data();
|
||||||
create_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
|
create_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
|
||||||
create_info.ppEnabledLayerNames = enabled_layers.data();
|
create_info.ppEnabledLayerNames = enabled_layers.data();
|
||||||
create_info.enabledExtensionCount =
|
create_info.enabledExtensionCount =
|
||||||
|
@ -205,31 +215,49 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
|
||||||
|
|
||||||
// Get the primary queue used for most submissions/etc.
|
// Get the primary queue used for most submissions/etc.
|
||||||
vkGetDeviceQueue(handle, queue_family_index_, 0, &primary_queue_);
|
vkGetDeviceQueue(handle, queue_family_index_, 0, &primary_queue_);
|
||||||
|
if (!primary_queue_) {
|
||||||
|
XELOGE("vkGetDeviceQueue returned nullptr!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Get all additional queues, if we got any.
|
// Get all additional queues, if we got any.
|
||||||
for (uint32_t i = 0; i < queue_count - 1; ++i) {
|
free_queues_.resize(device_info_.queue_family_properties.size());
|
||||||
VkQueue queue;
|
for (uint32_t i = 0; i < device_info_.queue_family_properties.size(); i++) {
|
||||||
vkGetDeviceQueue(handle, queue_family_index_, i, &queue);
|
VkQueueFamilyProperties& family_props =
|
||||||
free_queues_.push_back(queue);
|
device_info_.queue_family_properties[i];
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < family_props.queueCount; j++) {
|
||||||
|
VkQueue queue = nullptr;
|
||||||
|
if (i == queue_family_index_ && j == 0) {
|
||||||
|
// Already retrieved the primary queue index.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vkGetDeviceQueue(handle, i, j, &queue);
|
||||||
|
if (queue) {
|
||||||
|
free_queues_[i].push_back(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XELOGVK("Device initialized successfully!");
|
XELOGVK("Device initialized successfully!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkQueue VulkanDevice::AcquireQueue() {
|
VkQueue VulkanDevice::AcquireQueue(uint32_t queue_family_index) {
|
||||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||||
if (free_queues_.empty()) {
|
if (free_queues_[queue_family_index].empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto queue = free_queues_.back();
|
|
||||||
free_queues_.pop_back();
|
auto queue = free_queues_[queue_family_index].back();
|
||||||
|
free_queues_[queue_family_index].pop_back();
|
||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::ReleaseQueue(VkQueue queue) {
|
void VulkanDevice::ReleaseQueue(VkQueue queue, uint32_t queue_family_index) {
|
||||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||||
free_queues_.push_back(queue);
|
free_queues_[queue_family_index].push_back(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::DbgSetObjectName(VkDevice device, uint64_t object,
|
void VulkanDevice::DbgSetObjectName(VkDevice device, uint64_t object,
|
||||||
|
|
|
@ -70,10 +70,10 @@ class VulkanDevice {
|
||||||
// returns null the primary_queue should be used with the
|
// returns null the primary_queue should be used with the
|
||||||
// primary_queue_mutex.
|
// primary_queue_mutex.
|
||||||
// This method is thread safe.
|
// This method is thread safe.
|
||||||
VkQueue AcquireQueue();
|
VkQueue AcquireQueue(uint32_t queue_family_index);
|
||||||
// Releases a queue back to the device pool.
|
// Releases a queue back to the device pool.
|
||||||
// This method is thread safe.
|
// This method is thread safe.
|
||||||
void ReleaseQueue(VkQueue queue);
|
void ReleaseQueue(VkQueue queue, uint32_t queue_family_index);
|
||||||
|
|
||||||
static void DbgSetObjectName(VkDevice device, uint64_t object,
|
static void DbgSetObjectName(VkDevice device, uint64_t object,
|
||||||
VkDebugReportObjectTypeEXT object_type,
|
VkDebugReportObjectTypeEXT object_type,
|
||||||
|
@ -108,7 +108,7 @@ class VulkanDevice {
|
||||||
uint32_t queue_family_index_ = 0;
|
uint32_t queue_family_index_ = 0;
|
||||||
std::mutex queue_mutex_;
|
std::mutex queue_mutex_;
|
||||||
VkQueue primary_queue_ = nullptr;
|
VkQueue primary_queue_ = nullptr;
|
||||||
std::vector<VkQueue> free_queues_;
|
std::vector<std::vector<VkQueue>> free_queues_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|
|
@ -69,7 +69,7 @@ VulkanInstance::VulkanInstance() {
|
||||||
|
|
||||||
VulkanInstance::~VulkanInstance() { DestroyInstance(); }
|
VulkanInstance::~VulkanInstance() { DestroyInstance(); }
|
||||||
|
|
||||||
bool VulkanInstance::Initialize(Window* any_target_window) {
|
bool VulkanInstance::Initialize() {
|
||||||
auto version = Version::Parse(VK_API_VERSION);
|
auto version = Version::Parse(VK_API_VERSION);
|
||||||
XELOGVK("Initializing Vulkan %s...", version.pretty_string.c_str());
|
XELOGVK("Initializing Vulkan %s...", version.pretty_string.c_str());
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ bool VulkanInstance::Initialize(Window* any_target_window) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query available devices so that we can pick one.
|
// Query available devices so that we can pick one.
|
||||||
if (!QueryDevices(any_target_window)) {
|
if (!QueryDevices()) {
|
||||||
XELOGE("Failed to query devices");
|
XELOGE("Failed to query devices");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ void VulkanInstance::DisableDebugValidation() {
|
||||||
dbg_report_callback_ = nullptr;
|
dbg_report_callback_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanInstance::QueryDevices(Window* any_target_window) {
|
bool VulkanInstance::QueryDevices() {
|
||||||
// Get handles to all devices.
|
// Get handles to all devices.
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
std::vector<VkPhysicalDevice> device_handles;
|
std::vector<VkPhysicalDevice> device_handles;
|
||||||
|
@ -376,56 +376,6 @@ bool VulkanInstance::QueryDevices(Window* any_target_window) {
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(
|
vkGetPhysicalDeviceQueueFamilyProperties(
|
||||||
device_handle, &count, device_info.queue_family_properties.data());
|
device_handle, &count, device_info.queue_family_properties.data());
|
||||||
|
|
||||||
// Gather queue family presentation support.
|
|
||||||
// TODO(benvanik): move to swap chain?
|
|
||||||
VkSurfaceKHR any_surface = nullptr;
|
|
||||||
#if XE_PLATFORM_WIN32
|
|
||||||
VkWin32SurfaceCreateInfoKHR create_info;
|
|
||||||
create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
|
||||||
create_info.pNext = nullptr;
|
|
||||||
create_info.flags = 0;
|
|
||||||
create_info.hinstance =
|
|
||||||
static_cast<HINSTANCE>(any_target_window->native_platform_handle());
|
|
||||||
create_info.hwnd = static_cast<HWND>(any_target_window->native_handle());
|
|
||||||
err = vkCreateWin32SurfaceKHR(handle, &create_info, nullptr, &any_surface);
|
|
||||||
CheckResult(err, "vkCreateWin32SurfaceKHR");
|
|
||||||
#elif XE_PLATFORM_LINUX
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
GtkWidget* window_handle =
|
|
||||||
static_cast<GtkWidget*>(any_target_window->native_handle());
|
|
||||||
GdkDisplay* gdk_display = gtk_widget_get_display(window_handle);
|
|
||||||
assert(GDK_IS_X11_DISPLAY(gdk_display));
|
|
||||||
xcb_connection_t* connection =
|
|
||||||
XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display));
|
|
||||||
xcb_window_t window =
|
|
||||||
gdk_x11_window_get_xid(gtk_widget_get_window(window_handle));
|
|
||||||
VkXcbSurfaceCreateInfoKHR create_info;
|
|
||||||
create_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
|
|
||||||
create_info.pNext = nullptr;
|
|
||||||
create_info.flags = 0;
|
|
||||||
create_info.connection = static_cast<xcb_connection_t*>(
|
|
||||||
any_target_window->native_platform_handle());
|
|
||||||
create_info.window = static_cast<xcb_window_t>(window);
|
|
||||||
auto err =
|
|
||||||
vkCreateXcbSurfaceKHR(handle, &create_info, nullptr, &any_surface);
|
|
||||||
CheckResult(err, "vkCreateXcbSurfaceKHR");
|
|
||||||
#else
|
|
||||||
#error Unsupported GDK Backend on Linux.
|
|
||||||
#endif // GDK_WINDOWING_X11
|
|
||||||
#else
|
|
||||||
#error Platform not yet implemented.
|
|
||||||
#endif // XE_PLATFORM_WIN32
|
|
||||||
device_info.queue_family_supports_present.resize(
|
|
||||||
device_info.queue_family_properties.size());
|
|
||||||
for (size_t j = 0; j < device_info.queue_family_supports_present.size();
|
|
||||||
++j) {
|
|
||||||
err = vkGetPhysicalDeviceSurfaceSupportKHR(
|
|
||||||
device_handle, static_cast<uint32_t>(j), any_surface,
|
|
||||||
&device_info.queue_family_supports_present[j]);
|
|
||||||
CheckResult(err, "vkGetPhysicalDeviceSurfaceSupportKHR");
|
|
||||||
}
|
|
||||||
vkDestroySurfaceKHR(handle, any_surface, nullptr);
|
|
||||||
|
|
||||||
// Gather layers.
|
// Gather layers.
|
||||||
std::vector<VkLayerProperties> layer_properties;
|
std::vector<VkLayerProperties> layer_properties;
|
||||||
err = vkEnumerateDeviceLayerProperties(device_handle, &count, nullptr);
|
err = vkEnumerateDeviceLayerProperties(device_handle, &count, nullptr);
|
||||||
|
@ -552,8 +502,6 @@ void VulkanInstance::DumpDeviceInfo(const DeviceInfo& device_info) {
|
||||||
: "");
|
: "");
|
||||||
XELOGVK(" queueCount = %u", queue_props.queueCount);
|
XELOGVK(" queueCount = %u", queue_props.queueCount);
|
||||||
XELOGVK(" timestampValidBits = %u", queue_props.timestampValidBits);
|
XELOGVK(" timestampValidBits = %u", queue_props.timestampValidBits);
|
||||||
XELOGVK(" supportsPresent = %s",
|
|
||||||
device_info.queue_family_supports_present[j] ? "true" : "false");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XELOGVK(" Layers:");
|
XELOGVK(" Layers:");
|
||||||
|
|
|
@ -50,8 +50,7 @@ class VulkanInstance {
|
||||||
// preparing the instance for general use.
|
// preparing the instance for general use.
|
||||||
// If initialization succeeds it's likely that no more failures beyond runtime
|
// If initialization succeeds it's likely that no more failures beyond runtime
|
||||||
// issues will occur.
|
// issues will occur.
|
||||||
// TODO(benvanik): remove need for any_target_window - it's just for queries.
|
bool Initialize();
|
||||||
bool Initialize(Window* any_target_window);
|
|
||||||
|
|
||||||
// Returns a list of all available devices as detected during initialization.
|
// Returns a list of all available devices as detected during initialization.
|
||||||
const std::vector<DeviceInfo>& available_devices() const {
|
const std::vector<DeviceInfo>& available_devices() const {
|
||||||
|
@ -79,7 +78,7 @@ class VulkanInstance {
|
||||||
void DisableDebugValidation();
|
void DisableDebugValidation();
|
||||||
|
|
||||||
// Queries all available physical devices.
|
// Queries all available physical devices.
|
||||||
bool QueryDevices(Window* any_target_window);
|
bool QueryDevices();
|
||||||
|
|
||||||
void DumpLayers(const std::vector<LayerInfo>& layers, const char* indent);
|
void DumpLayers(const std::vector<LayerInfo>& layers, const char* indent);
|
||||||
void DumpExtensions(const std::vector<VkExtensionProperties>& extensions,
|
void DumpExtensions(const std::vector<VkExtensionProperties>& extensions,
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
|
|
||||||
std::unique_ptr<GraphicsProvider> VulkanProvider::Create(Window* main_window) {
|
std::unique_ptr<VulkanProvider> VulkanProvider::Create(Window* main_window) {
|
||||||
std::unique_ptr<VulkanProvider> provider(new VulkanProvider(main_window));
|
std::unique_ptr<VulkanProvider> provider(new VulkanProvider(main_window));
|
||||||
if (!provider->Initialize()) {
|
if (!provider->Initialize()) {
|
||||||
xe::FatalError(
|
xe::FatalError(
|
||||||
|
@ -35,7 +35,7 @@ std::unique_ptr<GraphicsProvider> VulkanProvider::Create(Window* main_window) {
|
||||||
"list of supported GPUs.");
|
"list of supported GPUs.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::unique_ptr<GraphicsProvider>(provider.release());
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanProvider::VulkanProvider(Window* main_window)
|
VulkanProvider::VulkanProvider(Window* main_window)
|
||||||
|
@ -56,7 +56,7 @@ bool VulkanProvider::Initialize() {
|
||||||
Version::Make(0, 0, 0), false);
|
Version::Make(0, 0, 0), false);
|
||||||
|
|
||||||
// Attempt initialization and device query.
|
// Attempt initialization and device query.
|
||||||
if (!instance_->Initialize(main_window_)) {
|
if (!instance_->Initialize()) {
|
||||||
XELOGE("Failed to initialize vulkan instance");
|
XELOGE("Failed to initialize vulkan instance");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
public:
|
public:
|
||||||
~VulkanProvider() override;
|
~VulkanProvider() override;
|
||||||
|
|
||||||
static std::unique_ptr<GraphicsProvider> Create(Window* main_window);
|
static std::unique_ptr<VulkanProvider> Create(Window* main_window);
|
||||||
|
|
||||||
VulkanInstance* instance() const { return instance_.get(); }
|
VulkanInstance* instance() const { return instance_.get(); }
|
||||||
VulkanDevice* device() const { return device_.get(); }
|
VulkanDevice* device() const { return device_.get(); }
|
||||||
|
|
|
@ -36,14 +36,40 @@ VulkanSwapChain::~VulkanSwapChain() { Shutdown(); }
|
||||||
|
|
||||||
VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
|
VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
|
||||||
surface_ = surface;
|
surface_ = surface;
|
||||||
|
VkResult status;
|
||||||
|
|
||||||
|
// Find a queue family that supports presentation.
|
||||||
VkBool32 surface_supported = false;
|
VkBool32 surface_supported = false;
|
||||||
auto status = vkGetPhysicalDeviceSurfaceSupportKHR(
|
uint32_t queue_family_index = -1;
|
||||||
*device_, device_->queue_family_index(), surface, &surface_supported);
|
for (uint32_t i = 0;
|
||||||
assert_true(surface_supported);
|
i < device_->device_info().queue_family_properties.size(); i++) {
|
||||||
CheckResult(status, "vkGetPhysicalDeviceSurfaceSupportKHR");
|
const VkQueueFamilyProperties& family_props =
|
||||||
if (status != VK_SUCCESS) {
|
device_->device_info().queue_family_properties[i];
|
||||||
return status;
|
if (!(family_props.queueFlags & VK_QUEUE_GRAPHICS_BIT) ||
|
||||||
|
!(family_props.queueFlags & VK_QUEUE_TRANSFER_BIT)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = vkGetPhysicalDeviceSurfaceSupportKHR(*device_, i, surface,
|
||||||
|
&surface_supported);
|
||||||
|
if (status == VK_SUCCESS && surface_supported == VK_TRUE) {
|
||||||
|
queue_family_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!surface_supported) {
|
||||||
|
XELOGE(
|
||||||
|
"Physical device does not have a queue that supports "
|
||||||
|
"graphics/transfer/presentation!");
|
||||||
|
return VK_ERROR_INCOMPATIBLE_DRIVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
presentation_queue_family_ = queue_family_index;
|
||||||
|
presentation_queue_ = device_->AcquireQueue(queue_family_index);
|
||||||
|
if (!presentation_queue_) {
|
||||||
|
XELOGE("Failed to acquire swap chain presentation queue!");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query supported target formats.
|
// Query supported target formats.
|
||||||
|
@ -190,7 +216,7 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
|
||||||
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
cmd_pool_info.pNext = nullptr;
|
cmd_pool_info.pNext = nullptr;
|
||||||
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
cmd_pool_info.queueFamilyIndex = device_->queue_family_index();
|
cmd_pool_info.queueFamilyIndex = presentation_queue_family_;
|
||||||
status = vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_);
|
status = vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_);
|
||||||
CheckResult(status, "vkCreateCommandPool");
|
CheckResult(status, "vkCreateCommandPool");
|
||||||
if (status != VK_SUCCESS) {
|
if (status != VK_SUCCESS) {
|
||||||
|
@ -432,6 +458,11 @@ void VulkanSwapChain::Shutdown() {
|
||||||
vkDestroyCommandPool(*device_, cmd_pool_, nullptr);
|
vkDestroyCommandPool(*device_, cmd_pool_, nullptr);
|
||||||
cmd_pool_ = nullptr;
|
cmd_pool_ = nullptr;
|
||||||
}
|
}
|
||||||
|
if (presentation_queue_) {
|
||||||
|
device_->ReleaseQueue(presentation_queue_, presentation_queue_family_);
|
||||||
|
presentation_queue_ = nullptr;
|
||||||
|
presentation_queue_family_ = -1;
|
||||||
|
}
|
||||||
// images_ doesn't need to be cleaned up as the swapchain does it implicitly.
|
// images_ doesn't need to be cleaned up as the swapchain does it implicitly.
|
||||||
if (handle) {
|
if (handle) {
|
||||||
vkDestroySwapchainKHR(*device_, handle, nullptr);
|
vkDestroySwapchainKHR(*device_, handle, nullptr);
|
||||||
|
@ -471,11 +502,7 @@ VkResult VulkanSwapChain::Begin() {
|
||||||
wait_submit_info.pCommandBuffers = nullptr;
|
wait_submit_info.pCommandBuffers = nullptr;
|
||||||
wait_submit_info.signalSemaphoreCount = 1;
|
wait_submit_info.signalSemaphoreCount = 1;
|
||||||
wait_submit_info.pSignalSemaphores = &image_usage_semaphore_;
|
wait_submit_info.pSignalSemaphores = &image_usage_semaphore_;
|
||||||
{
|
status = vkQueueSubmit(presentation_queue_, 1, &wait_submit_info, nullptr);
|
||||||
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
|
||||||
status =
|
|
||||||
vkQueueSubmit(device_->primary_queue(), 1, &wait_submit_info, nullptr);
|
|
||||||
}
|
|
||||||
if (status != VK_SUCCESS) {
|
if (status != VK_SUCCESS) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -687,12 +714,7 @@ VkResult VulkanSwapChain::End() {
|
||||||
render_submit_info.pCommandBuffers = &cmd_buffer_;
|
render_submit_info.pCommandBuffers = &cmd_buffer_;
|
||||||
render_submit_info.signalSemaphoreCount = uint32_t(semaphores.size()) - 1;
|
render_submit_info.signalSemaphoreCount = uint32_t(semaphores.size()) - 1;
|
||||||
render_submit_info.pSignalSemaphores = semaphores.data();
|
render_submit_info.pSignalSemaphores = semaphores.data();
|
||||||
{
|
status = vkQueueSubmit(presentation_queue_, 1, &render_submit_info, nullptr);
|
||||||
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
|
||||||
status = vkQueueSubmit(device_->primary_queue(), 1, &render_submit_info,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != VK_SUCCESS) {
|
if (status != VK_SUCCESS) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -709,10 +731,7 @@ VkResult VulkanSwapChain::End() {
|
||||||
present_info.pSwapchains = swap_chains;
|
present_info.pSwapchains = swap_chains;
|
||||||
present_info.pImageIndices = swap_chain_image_indices;
|
present_info.pImageIndices = swap_chain_image_indices;
|
||||||
present_info.pResults = nullptr;
|
present_info.pResults = nullptr;
|
||||||
{
|
status = vkQueuePresentKHR(presentation_queue_, &present_info);
|
||||||
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
|
||||||
status = vkQueuePresentKHR(device_->primary_queue(), &present_info);
|
|
||||||
}
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case VK_SUCCESS:
|
case VK_SUCCESS:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -78,6 +78,8 @@ class VulkanSwapChain {
|
||||||
VulkanInstance* instance_ = nullptr;
|
VulkanInstance* instance_ = nullptr;
|
||||||
VulkanDevice* device_ = nullptr;
|
VulkanDevice* device_ = nullptr;
|
||||||
|
|
||||||
|
VkQueue presentation_queue_ = nullptr;
|
||||||
|
uint32_t presentation_queue_family_ = -1;
|
||||||
VkSurfaceKHR surface_ = nullptr;
|
VkSurfaceKHR surface_ = nullptr;
|
||||||
uint32_t surface_width_ = 0;
|
uint32_t surface_width_ = 0;
|
||||||
uint32_t surface_height_ = 0;
|
uint32_t surface_height_ = 0;
|
||||||
|
|
|
@ -95,7 +95,6 @@ struct DeviceInfo {
|
||||||
VkPhysicalDeviceFeatures features;
|
VkPhysicalDeviceFeatures features;
|
||||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||||
std::vector<VkQueueFamilyProperties> queue_family_properties;
|
std::vector<VkQueueFamilyProperties> queue_family_properties;
|
||||||
std::vector<VkBool32> queue_family_supports_present;
|
|
||||||
std::vector<LayerInfo> layers;
|
std::vector<LayerInfo> layers;
|
||||||
std::vector<VkExtensionProperties> extensions;
|
std::vector<VkExtensionProperties> extensions;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue