blob: 9fff8fa31cb90d15a2e3ac57585264e2c64b95eb [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/lib/escher/vk/vulkan_device_queues.h"
#include <lib/syslog/cpp/macros.h>
#include <iterator>
#include <set>
#include "src/ui/lib/escher/impl/vulkan_utils.h"
#include "vulkan/vulkan_structs.hpp"
namespace escher {
VulkanDeviceQueues::Caps::Caps(vk::PhysicalDevice device) {
{
vk::PhysicalDeviceProperties props = device.getProperties();
max_image_width = props.limits.maxImageDimension2D;
max_image_height = props.limits.maxImageDimension2D;
device_api_version = props.apiVersion;
msaa_sample_counts =
impl::GetSupportedColorSampleCounts(props.limits.sampledImageColorSampleCounts);
}
{
auto formats = impl::GetSupportedDepthFormats(device, {
vk::Format::eD16Unorm,
vk::Format::eX8D24UnormPack32,
vk::Format::eD32Sfloat,
vk::Format::eS8Uint,
vk::Format::eD16UnormS8Uint,
vk::Format::eD24UnormS8Uint,
vk::Format::eD32SfloatS8Uint,
});
depth_stencil_formats.insert(formats.begin(), formats.end());
}
}
vk::ResultValue<vk::Format> VulkanDeviceQueues::Caps::GetMatchingDepthStencilFormat(
const std::vector<vk::Format>& formats) const {
for (auto fmt : formats) {
if (depth_stencil_formats.find(fmt) != depth_stencil_formats.end()) {
return {vk::Result::eSuccess, fmt};
}
}
return {vk::Result::eErrorFeatureNotPresent, vk::Format::eUndefined};
}
vk::ResultValue<size_t> VulkanDeviceQueues::Caps::GetMatchingSampleCount(
const std::vector<size_t>& counts) const {
for (auto count : counts) {
if (msaa_sample_counts.find(count) != msaa_sample_counts.end()) {
return {vk::Result::eSuccess, count};
}
}
return {vk::Result::eErrorFeatureNotPresent, 0};
}
std::set<vk::Format> VulkanDeviceQueues::Caps::GetAllMatchingDepthStencilFormats(
const std::set<vk::Format>& formats) const {
std::set<vk::Format> result;
std::set_intersection(formats.begin(), formats.end(), depth_stencil_formats.begin(),
depth_stencil_formats.end(), std::inserter(result, result.begin()));
return result;
}
std::set<size_t> VulkanDeviceQueues::Caps::GetAllMatchingSampleCounts(
const std::set<size_t>& counts) const {
std::set<size_t> result;
std::set_intersection(counts.begin(), counts.end(), msaa_sample_counts.begin(),
msaa_sample_counts.end(), std::inserter(result, result.begin()));
return result;
}
namespace {
// Helper for PopulateProcAddrs().
template <typename FuncT>
static FuncT GetDeviceProcAddr(vk::Device device, const char* func_name) {
FuncT func = reinterpret_cast<FuncT>(device.getProcAddr(func_name));
FX_CHECK(func) << "failed to find function address for: " << func_name;
return func;
}
// Helper for VulkanDeviceQueues constructor.
VulkanDeviceQueues::ProcAddrs PopulateProcAddrs(vk::Device device,
const VulkanDeviceQueues::Params& params) {
#define GET_DEVICE_PROC_ADDR(XXX) result.XXX = GetDeviceProcAddr<PFN_vk##XXX>(device, "vk" #XXX)
VulkanDeviceQueues::ProcAddrs result;
if (params.required_extension_names.find(VK_KHR_SWAPCHAIN_EXTENSION_NAME) !=
params.required_extension_names.end() ||
params.desired_extension_names.find(VK_KHR_SWAPCHAIN_EXTENSION_NAME) !=
params.desired_extension_names.end()) {
GET_DEVICE_PROC_ADDR(CreateSwapchainKHR);
GET_DEVICE_PROC_ADDR(DestroySwapchainKHR);
GET_DEVICE_PROC_ADDR(GetSwapchainImagesKHR);
GET_DEVICE_PROC_ADDR(AcquireNextImageKHR);
GET_DEVICE_PROC_ADDR(QueuePresentKHR);
}
return result;
#undef GET_DEVICE_PROC_ADDR
}
// Return value for FindSuitablePhysicalDeviceAndQueueFamilies(). Valid if and
// only if device is non-null. Otherwise, no suitable device was found.
struct SuitablePhysicalDeviceAndQueueFamilies {
vk::PhysicalDevice physical_device;
uint32_t main_queue_family = VK_QUEUE_FAMILY_IGNORED;
uint32_t transfer_queue_family = VK_QUEUE_FAMILY_IGNORED;
std::set<vk::QueueGlobalPriorityEXT> main_queue_family_priorities;
};
// Helper for GatherExtensions(). There are some extension names which we disallow; return false
// if we try to use one.
bool CheckExtensionAgainstDenyList(const std::string& name) {
// These two extensions are deprecated after being promoted to VK_KHR_global_priority.
// Elsewhere, if the KHR extension is requested, we take additional actions, but we don't
// if the EXT extensions are requested. So to avoid confusion, we simply disallow the EXT
// extensions.
if (name == VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME ||
name == VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME) {
FX_LOGS(ERROR) << "VK_EXT_global_priority/VK_EXT_global_priority_query are DISALLOWED. "
<< "Use VK_KHR_global_priority instead.";
return false;
}
return true;
}
// Helper for GatherExtensions().
bool ValidateExtension(vk::PhysicalDevice device, const std::string& name,
const std::vector<vk::ExtensionProperties>& base_extensions,
const std::set<std::string>& required_layer_names) {
if (!CheckExtensionAgainstDenyList(name)) {
return false;
}
auto found = std::find_if(base_extensions.begin(), base_extensions.end(),
[&name](const vk::ExtensionProperties& extension) {
return !strncmp(extension.extensionName, name.c_str(),
VK_MAX_EXTENSION_NAME_SIZE);
});
if (found != base_extensions.end())
return true;
// Didn't find the extension in the base list of extensions. Perhaps it is
// implemented in a layer.
for (auto& layer_name : required_layer_names) {
auto layer_extensions =
ESCHER_CHECKED_VK_RESULT(device.enumerateDeviceExtensionProperties(layer_name));
auto found = std::find_if(layer_extensions.begin(), layer_extensions.end(),
[&name](vk::ExtensionProperties& extension) {
return !strncmp(extension.extensionName, name.c_str(),
VK_MAX_EXTENSION_NAME_SIZE);
});
if (found != layer_extensions.end())
return true;
}
return false;
}
// Helper struct returned by `GatherExtensions()`.
struct Extensions {
// Same device passed as an arg to `GatherExtensions()`.
vk::PhysicalDevice device;
// The requested extensions that are available on `device`. This is the union of both required
// and desired (optional) extensions. The device may support additional extensions, but the
// ones returned here are filtered to only include requested extensions.
std::set<std::string> available;
// Optional extensions which were requested, but are not supported by `device`.
std::set<std::string> missing_optional;
};
// If `device` supports all of the required extensions and layers, return an `Extensions` struct
// (see the comments on the fields of that struct). Returns nothing if there are ANY required
// extensions/layers which are not supported by `device`.
std::optional<Extensions> GatherExtensions(vk::PhysicalDevice device,
const std::set<std::string>& required_extension_names,
const std::set<std::string>& desired_extension_names,
const std::set<std::string>& required_layer_names) {
auto extensions = ESCHER_CHECKED_VK_RESULT(device.enumerateDeviceExtensionProperties());
std::set<std::string> available_extension_names;
std::set<std::string> missing_required_extension_names;
std::set<std::string> missing_optional_extension_names;
// Classify all required extensions as either available or missing.
for (auto& name : required_extension_names) {
if (ValidateExtension(device, name, extensions, required_layer_names)) {
available_extension_names.insert(name);
} else {
missing_required_extension_names.insert(name);
}
}
// If any required extensions were not available, log the missing ones for easy debugging, then
// return nothing.
if (!missing_required_extension_names.empty()) {
FX_LOGS(INFO) << "Vulkan physical device is missing required extensions:";
for (auto& name : missing_required_extension_names) {
FX_LOGS(INFO) << " - " << name;
}
FX_LOGS(INFO) << "... skipping.";
return {};
}
// All required extensions were found. Now check which desired/optional ones are available.
for (auto& name : desired_extension_names) {
if (ValidateExtension(device, name, extensions, required_layer_names)) {
available_extension_names.insert(name);
} else {
missing_optional_extension_names.insert(name);
}
}
return {{device, available_extension_names, missing_optional_extension_names}};
}
SuitablePhysicalDeviceAndQueueFamilies FindSuitablePhysicalDeviceAndQueueFamilies(
const VulkanInstancePtr& instance, const VulkanDeviceQueues::Params& params) {
auto physical_devices =
ESCHER_CHECKED_VK_RESULT(instance->vk_instance().enumeratePhysicalDevices());
// A suitable main queue needs to support graphics and compute.
const auto kMainQueueFlags = vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute;
// A specialized transfer queue will only support transfer; see comment below,
// where these flags are used.
const auto kTransferQueueFlags =
vk::QueueFlagBits::eTransfer | vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute;
// Look for a physical device that has both all required extensions and all desired extensions.
// If one is found, we look no further. If any required extensions are missing, skip that device.
// If only optional extensions are missing, remember this device; we will use it if we fail to
// find one that has all required/desired extensions.
//
// Note the weakness of this algorithm: maybe the first device is only missing one optional
// extension, but a subsequent device doesn't support any optional extensions. In such a case,
// the first device might be a better choice but will not be chosen (because we only remember the
// most recent device which supports all required extensions). It doesn't make sense to be
// smarter here because:
// - we usually have only one physical device anyway
// - when choosing between two devices, there are other possible metrics than the number of
// optional extensions that each supports. For example, one device might have twice the VRAM
// or compute core, yet support less extensions.
std::optional<Extensions> extensions;
{
for (auto& physical_device : physical_devices) {
auto maybe_extensions =
GatherExtensions(physical_device, params.required_extension_names,
params.desired_extension_names, instance->params().layer_names);
if (maybe_extensions) {
extensions = std::move(maybe_extensions);
if (extensions->missing_optional.empty()) {
// Hooray! We found a device which supports all required and optional extensions.
break;
}
}
}
if (!extensions) {
// No devices had all of the required extensions.
return {vk::PhysicalDevice(), VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED};
}
if (!extensions->missing_optional.empty()) {
FX_LOGS(INFO)
<< "Found Vulkan physical device with all required extensions, but missing optional extensions:";
for (auto& name : extensions->missing_optional) {
FX_LOGS(INFO) << " - " << name;
}
}
}
// At this point we've chosen the physical device we're going to use.
{
auto& physical_device = extensions->device;
auto& extension_names = extensions->available;
// Find the main queue family. If none is found, continue on to the next
// physical device.
const bool filter_queues_for_present =
params.surface &&
!(params.flags & VulkanDeviceQueues::Params::kDisableQueueFilteringForPresent);
uint32_t queue_family_count;
physical_device.getQueueFamilyProperties2(&queue_family_count, nullptr);
// We know how many queue families there are. For each one, query their generic properties,
// and also extra properties relating to global priorities (but only if the global priorities
// extension is available). The latter is achieved by adding an additional struct into the
// former's pNext chain.
std::vector<vk::QueueFamilyProperties2> properties(queue_family_count);
std::vector<vk::QueueFamilyGlobalPriorityPropertiesKHR> global_priority_properties(
queue_family_count);
const bool supports_queue_global_priorities =
std::find(extension_names.begin(), extension_names.end(),
VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME) != extension_names.end();
if (supports_queue_global_priorities) {
// For each queue family that we are querying, add a separate pNext struct to query the
// global priority properties for that same queue family.
for (size_t i = 0; i < queue_family_count; ++i) {
properties[i].pNext = &global_priority_properties[i];
}
}
physical_device.getQueueFamilyProperties2(&queue_family_count, properties.data());
for (uint32_t i = 0; i < queue_family_count; ++i) {
auto& props = properties[i].queueFamilyProperties;
if (kMainQueueFlags == (props.queueFlags & kMainQueueFlags)) {
if (filter_queues_for_present) {
// TODO: it is possible that there is no queue family that supports
// both graphics/compute and present. In this case, we would need a
// separate present queue. For now, just look for a single queue that
// meets all of our needs.
auto get_surface_support_result =
physical_device.getSurfaceSupportKHR(i, params.surface, instance->proc_addrs());
FX_CHECK(get_surface_support_result.result == vk::Result::eSuccess);
VkBool32 supports_present = get_surface_support_result.value;
if (supports_present != VK_TRUE) {
FX_LOGS(INFO) << "Queue supports graphics/compute, but not presentation";
continue;
}
}
// At this point, we have already succeeded. Now, try to find the optimal transfer queue
// family (if we can't, we'll just use the main queue for transfer operations).
SuitablePhysicalDeviceAndQueueFamilies result;
result.physical_device = physical_device;
result.main_queue_family = i;
result.transfer_queue_family = i;
for (uint32_t j = 0; j < queue_family_count; ++j) {
if ((props.queueFlags & kTransferQueueFlags) == vk::QueueFlagBits::eTransfer) {
// We have found a transfer-only queue. This is the fastest way to
// upload data to the GPU.
result.transfer_queue_family = j;
break;
}
}
// Remember the available priorities for the main queue family.
for (uint32_t k = 0; k < global_priority_properties[i].priorityCount; ++k) {
result.main_queue_family_priorities.insert(global_priority_properties[i].priorities[k]);
}
return result;
}
}
}
return {vk::PhysicalDevice(), VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED};
}
} // namespace
fxl::RefPtr<VulkanDeviceQueues> VulkanDeviceQueues::New(VulkanInstancePtr instance, Params params) {
// Escher requires the memory_requirements_2 extension for the
// vma_gpu_allocator to function.
params.required_extension_names.insert(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
// Required for use of image barrier queue family index VK_QUEUE_FAMILY_FOREIGN_EXT
params.required_extension_names.insert(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
// If the params contain a surface, then ensure that the swapchain extension
// is supported so that we can render to that surface.
if (params.surface) {
params.required_extension_names.insert(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
}
#if defined(OS_FUCHSIA)
// If we're running on Fuchsia, make sure we have our semaphore extensions.
params.required_extension_names.insert(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
params.required_extension_names.insert(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
#endif
vk::PhysicalDevice physical_device;
uint32_t main_queue_family = VK_QUEUE_FAMILY_IGNORED;
uint32_t transfer_queue_family = VK_QUEUE_FAMILY_IGNORED;
SuitablePhysicalDeviceAndQueueFamilies chosen_device_and_queues =
FindSuitablePhysicalDeviceAndQueueFamilies(instance, params);
FX_CHECK(chosen_device_and_queues.physical_device)
<< "Unable to find a suitable physical device.";
FX_CHECK(chosen_device_and_queues.main_queue_family != VK_QUEUE_FAMILY_IGNORED);
FX_CHECK(chosen_device_and_queues.transfer_queue_family != VK_QUEUE_FAMILY_IGNORED);
physical_device = chosen_device_and_queues.physical_device;
main_queue_family = chosen_device_and_queues.main_queue_family;
transfer_queue_family = chosen_device_and_queues.transfer_queue_family;
{
auto props = physical_device.getProperties();
FX_LOGS(INFO) << "Using physical device: " << props.deviceName
<< " (apiVersion: " << VK_VERSION_MAJOR(props.apiVersion) << "."
<< VK_VERSION_MINOR(props.apiVersion) << "." << VK_VERSION_PATCH(props.apiVersion)
<< " driverVersion: " << VK_VERSION_MAJOR(props.driverVersion) << "."
<< VK_VERSION_MINOR(props.driverVersion) << "."
<< VK_VERSION_PATCH(props.driverVersion) << ")";
}
// Partially populate device capabilities from the physical device.
// Other stuff (e.g. which extensions are supported) will be added below.
VulkanDeviceQueues::Caps caps(physical_device);
auto physical_device_features = vk::PhysicalDeviceFeatures2();
// We first use these structs to retrieve features from the physical device, and later
// repurpose them below by adding them to the pNext chain of `VkDeviceCreateInfo`.
auto physical_device_memory_features = vk::PhysicalDeviceProtectedMemoryFeatures();
auto sampler_ycbcr_conversion_features = vk::PhysicalDeviceSamplerYcbcrConversionFeatures();
physical_device_features.setPNext(&physical_device_memory_features);
physical_device_memory_features.setPNext(&sampler_ycbcr_conversion_features);
physical_device.getFeatures2(&physical_device_features);
// Get the maximum supported Vulkan API version (minimum of device version and instance version).
uint32_t max_api_version = std::min(caps.device_api_version, instance->api_version());
// Protected memory is only supported with Vulkan API version 1.1.
if (!physical_device_memory_features.protectedMemory || max_api_version < VK_API_VERSION_1_1) {
FX_LOGS(INFO) << "Protected memory is not supported.";
caps.allow_protected_memory = false;
} else {
caps.allow_protected_memory = params.flags & VulkanDeviceQueues::Params::kAllowProtectedMemory;
if (caps.allow_protected_memory) {
FX_LOGS(INFO) << "Protected memory is enabled.";
} else {
FX_LOGS(INFO) << "Protected memory is supported but not enabled.";
}
}
// Prepare to create the Device and Queues.
vk::DeviceQueueCreateInfo queue_info[2];
const float kQueuePriority = 0;
queue_info[0] = vk::DeviceQueueCreateInfo();
queue_info[0].queueFamilyIndex = main_queue_family;
queue_info[0].flags = caps.allow_protected_memory ? vk::DeviceQueueCreateFlagBits::eProtected
: vk::DeviceQueueCreateFlags();
queue_info[0].queueCount = 1;
queue_info[0].pQueuePriorities = &kQueuePriority;
queue_info[1] = vk::DeviceQueueCreateInfo();
queue_info[1].queueFamilyIndex = transfer_queue_family;
queue_info[1].queueCount = 1;
queue_info[1].pQueuePriorities = &kQueuePriority;
// Prepare the list of extension names that will be used to create the device.
{
// These extensions were already validated by FindSuitablePhysicalDeviceAndQueueFamilies();
caps.extensions = params.required_extension_names;
// Request as many of the desired (but optional) extensions as possible.
auto extensions =
ESCHER_CHECKED_VK_RESULT(physical_device.enumerateDeviceExtensionProperties());
std::set<std::string> available_desired_extensions;
for (auto& name : params.desired_extension_names) {
if (ValidateExtension(physical_device, name, extensions, instance->params().layer_names)) {
caps.extensions.insert(name);
}
}
}
std::vector<const char*> extension_names;
for (auto& extension : caps.extensions) {
extension_names.push_back(extension.c_str());
}
caps.allow_ycbcr =
sampler_ycbcr_conversion_features.samplerYcbcrConversion &&
caps.extensions.find(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME) != caps.extensions.end();
// Specify the required physical device features, and verify that they are all
// supported.
// TODO(https://fxbug.dev/42151436): instead of hard-coding the required features here, provide a
// mechanism for Escher clients to specify additional required features.
vk::PhysicalDeviceFeatures supported_device_features;
physical_device.getFeatures(&supported_device_features);
bool device_has_all_required_features = true;
#define ADD_DESIRED_FEATURE(X) \
if (supported_device_features.X) { \
caps.enabled_features.X = true; \
} else { \
FX_LOGS(INFO) << "Desired Vulkan Device feature not supported: " << #X; \
}
#define ADD_REQUIRED_FEATURE(X) \
caps.enabled_features.X = true; \
if (!supported_device_features.X) { \
FX_LOGS(ERROR) << "Required Vulkan Device feature not supported: " << #X; \
device_has_all_required_features = false; \
}
// TODO(https://fxbug.dev/42081100): We would like to make 'shaderClipDistance' a requirement on
// all Scenic platforms. For now, treat it as a DESIRED_FEATURE.
ADD_DESIRED_FEATURE(shaderClipDistance);
ADD_DESIRED_FEATURE(fillModeNonSolid);
#undef ADD_DESIRED_FEATURE
#undef ADD_REQUIRED_FEATURE
if (!device_has_all_required_features) {
return fxl::RefPtr<VulkanDeviceQueues>();
}
// Insert NEXT as the first struct in BASE's "pNext chain". In other words, if this is called
// repeatedly, then the structs in the chain are in the reverse of the order they are added.
#define INSERT_PNEXT(BASE, NEXT) \
{ \
(NEXT).pNext = const_cast<void*>((BASE).pNext); \
(BASE).pNext = &(NEXT); \
}
// Almost ready to create the device; start populating the VkDeviceCreateInfo.
vk::DeviceCreateInfo device_info;
device_info.queueCreateInfoCount = 2;
device_info.pQueueCreateInfos = queue_info;
device_info.enabledExtensionCount = static_cast<uint32_t>(extension_names.size());
device_info.ppEnabledExtensionNames = extension_names.data();
device_info.pEnabledFeatures = &caps.enabled_features;
if (caps.allow_protected_memory) {
INSERT_PNEXT(device_info, physical_device_memory_features);
}
if (caps.allow_ycbcr) {
INSERT_PNEXT(device_info, sampler_ycbcr_conversion_features);
}
// It's possible that the main queue and transfer queue are in the same
// queue family. Adjust the device-creation parameters to account for this.
uint32_t main_queue_index = 0;
uint32_t transfer_queue_index = 0;
if (main_queue_family == transfer_queue_family) {
#if 0
// TODO: it may be worthwhile to create multiple queues in the same family.
// However, we would need to look at VkQueueFamilyProperties.queueCount to
// make sure that we can create multiple queues for that family. For now,
// it is easier to share a single queue when the main/transfer queues are in
// the same family.
queue_info[0].queueCount = 2;
device_info.queueCreateInfoCount = 1;
transfer_queue_index = 1;
#else
device_info.queueCreateInfoCount = 1;
#endif
}
// Try to assign a higher priority to the created queue. This will only occur if the caller
// requested the VK_KHR_global_priority extension.
vk::DeviceQueueGlobalPriorityCreateInfoKHR global_priority_create_info;
if (chosen_device_and_queues.main_queue_family_priorities.find(
vk::QueueGlobalPriorityEXT::eRealtime) !=
chosen_device_and_queues.main_queue_family_priorities.end()) {
global_priority_create_info.globalPriority = vk::QueueGlobalPriorityEXT::eRealtime;
INSERT_PNEXT(queue_info[0], global_priority_create_info);
FX_LOGS(INFO) << "Using main queue with Realtime global priority";
} else if (chosen_device_and_queues.main_queue_family_priorities.find(
vk::QueueGlobalPriorityEXT::eHigh) !=
chosen_device_and_queues.main_queue_family_priorities.end()) {
global_priority_create_info.globalPriority = vk::QueueGlobalPriorityEXT::eHigh;
INSERT_PNEXT(queue_info[0], global_priority_create_info);
FX_LOGS(INFO) << "Using main queue with High global priority";
}
#undef INSERT_PNEXT
// Create the device.
auto result = physical_device.createDevice(device_info);
if (result.result != vk::Result::eSuccess) {
FX_LOGS(WARNING) << "Could not create Vulkan Device: " << vk::to_string(result.result) << ".";
return fxl::RefPtr<VulkanDeviceQueues>();
}
vk::Device device = result.value;
// Obtain the queues that we requested to be created with the device.
vk::Queue main_queue;
if (caps.allow_protected_memory) {
auto main_queue_info2 = vk::DeviceQueueInfo2()
.setFlags(vk::DeviceQueueCreateFlagBits::eProtected)
.setQueueFamilyIndex(main_queue_family)
.setQueueIndex(main_queue_index);
main_queue = device.getQueue2(main_queue_info2);
} else {
main_queue = device.getQueue(main_queue_family, main_queue_index);
}
vk::Queue transfer_queue;
if (main_queue_family == transfer_queue_family && main_queue_index == transfer_queue_index) {
transfer_queue = main_queue;
} else {
transfer_queue = device.getQueue(transfer_queue_family, transfer_queue_index);
}
return fxl::AdoptRef(new VulkanDeviceQueues(
device, physical_device, main_queue, main_queue_family, transfer_queue, transfer_queue_family,
std::move(instance), std::move(params), std::move(caps)));
}
VulkanDeviceQueues::VulkanDeviceQueues(vk::Device device, vk::PhysicalDevice physical_device,
vk::Queue main_queue, uint32_t main_queue_family,
vk::Queue transfer_queue, uint32_t transfer_queue_family,
VulkanInstancePtr instance, Params params, Caps caps)
: device_(device),
physical_device_(physical_device),
main_queue_(main_queue),
main_queue_family_(main_queue_family),
transfer_queue_(transfer_queue),
transfer_queue_family_(transfer_queue_family),
instance_(std::move(instance)),
params_(std::move(params)),
caps_(std::move(caps)),
proc_addrs_(PopulateProcAddrs(device_, params_)) {
dispatch_loader_.init(instance_->vk_instance(), device_);
}
VulkanDeviceQueues::~VulkanDeviceQueues() { device_.destroy(); }
VulkanContext VulkanDeviceQueues::GetVulkanContext() const {
return escher::VulkanContext(instance_->vk_instance(), vk_physical_device(), vk_device(),
dispatch_loader(), vk_main_queue(), vk_main_queue_family(),
vk_transfer_queue(), vk_transfer_queue_family());
}
} // namespace escher