You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1195 lines
39 KiB
1195 lines
39 KiB
/*
|
|
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
#include <algorithm>
|
|
#include <nvh/nvprint.hpp>
|
|
#include <regex>
|
|
|
|
#include "context_vk.hpp"
|
|
#include "error_vk.hpp"
|
|
#include "extensions_vk.hpp"
|
|
|
|
#include <nvvk/debug_util_vk.hpp>
|
|
#include <nvp/perproject_globals.hpp>
|
|
|
|
namespace nvvk {
|
|
|
|
// Would be used in Context::debugMessengerCallback.
|
|
#if 0
|
|
static std::string ObjectTypeToString(VkObjectType value)
|
|
{
|
|
switch(value)
|
|
{
|
|
case VK_OBJECT_TYPE_UNKNOWN:
|
|
return "Unknown";
|
|
case VK_OBJECT_TYPE_INSTANCE:
|
|
return "Instance";
|
|
case VK_OBJECT_TYPE_PHYSICAL_DEVICE:
|
|
return "PhysicalDevice";
|
|
case VK_OBJECT_TYPE_DEVICE:
|
|
return "Device";
|
|
case VK_OBJECT_TYPE_QUEUE:
|
|
return "Queue";
|
|
case VK_OBJECT_TYPE_SEMAPHORE:
|
|
return "Semaphore";
|
|
case VK_OBJECT_TYPE_COMMAND_BUFFER:
|
|
return "CommandBuffer";
|
|
case VK_OBJECT_TYPE_FENCE:
|
|
return "Fence";
|
|
case VK_OBJECT_TYPE_DEVICE_MEMORY:
|
|
return "DeviceMemory";
|
|
case VK_OBJECT_TYPE_BUFFER:
|
|
return "Buffer";
|
|
case VK_OBJECT_TYPE_IMAGE:
|
|
return "Image";
|
|
case VK_OBJECT_TYPE_EVENT:
|
|
return "Event";
|
|
case VK_OBJECT_TYPE_QUERY_POOL:
|
|
return "QueryPool";
|
|
case VK_OBJECT_TYPE_BUFFER_VIEW:
|
|
return "BufferView";
|
|
case VK_OBJECT_TYPE_IMAGE_VIEW:
|
|
return "ImageView";
|
|
case VK_OBJECT_TYPE_SHADER_MODULE:
|
|
return "ShaderModule";
|
|
case VK_OBJECT_TYPE_PIPELINE_CACHE:
|
|
return "PipelineCache";
|
|
case VK_OBJECT_TYPE_PIPELINE_LAYOUT:
|
|
return "PipelineLayout";
|
|
case VK_OBJECT_TYPE_RENDER_PASS:
|
|
return "RenderPass";
|
|
case VK_OBJECT_TYPE_PIPELINE:
|
|
return "Pipeline";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT:
|
|
return "DescriptorSetLayout";
|
|
case VK_OBJECT_TYPE_SAMPLER:
|
|
return "Sampler";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_POOL:
|
|
return "DescriptorPool";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_SET:
|
|
return "DescriptorSet";
|
|
case VK_OBJECT_TYPE_FRAMEBUFFER:
|
|
return "Framebuffer";
|
|
case VK_OBJECT_TYPE_COMMAND_POOL:
|
|
return "CommandPool";
|
|
case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:
|
|
return "SamplerYcbcrConversion";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE:
|
|
return "DescriptorUpdateTemplate";
|
|
case VK_OBJECT_TYPE_SURFACE_KHR:
|
|
return "SurfaceKHR";
|
|
case VK_OBJECT_TYPE_SWAPCHAIN_KHR:
|
|
return "SwapchainKHR";
|
|
case VK_OBJECT_TYPE_DISPLAY_KHR:
|
|
return "DisplayKHR";
|
|
case VK_OBJECT_TYPE_DISPLAY_MODE_KHR:
|
|
return "DisplayModeKHR";
|
|
case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
|
|
return "DebugReportCallbackEXT";
|
|
#if VK_NV_device_generated_commands
|
|
case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV:
|
|
return "IndirectCommandsLayoutNV";
|
|
#endif
|
|
case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT:
|
|
return "DebugUtilsMessengerEXT";
|
|
case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT:
|
|
return "ValidationCacheEXT";
|
|
#if VK_NV_ray_tracing
|
|
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV:
|
|
return "AccelerationStructureNV";
|
|
#endif
|
|
default:
|
|
return "invalid";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Define a callback to capture the messages
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL Context::debugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
|
const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
|
|
void* userData)
|
|
{
|
|
Context* ctx = reinterpret_cast<Context*>(userData);
|
|
|
|
if(ctx->m_dbgIgnoreMessages.find(callbackData->messageIdNumber) != ctx->m_dbgIgnoreMessages.end())
|
|
return VK_FALSE;
|
|
|
|
// Check for severity: default ERROR and WARNING
|
|
if((ctx->m_dbgSeverity & messageSeverity) != messageSeverity)
|
|
return VK_FALSE;
|
|
|
|
int level = LOGLEVEL_INFO;
|
|
// repeating nvprintfLevel to help with breakpoints : so we can selectively break right after the print
|
|
if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
|
|
{
|
|
nvprintfLevel(level, "VERBOSE: %s \n --> %s\n", callbackData->pMessageIdName, callbackData->pMessage);
|
|
}
|
|
else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
|
|
{
|
|
nvprintfLevel(level, "INFO: %s \n --> %s\n", callbackData->pMessageIdName, callbackData->pMessage);
|
|
}
|
|
else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
|
|
{
|
|
level = LOGLEVEL_WARNING;
|
|
nvprintfLevel(level, "WARNING: %s \n --> %s\n", callbackData->pMessageIdName, callbackData->pMessage);
|
|
}
|
|
else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
|
|
{
|
|
level = LOGLEVEL_ERROR;
|
|
nvprintfLevel(level, "ERROR: %s \n --> %s\n", callbackData->pMessageIdName, callbackData->pMessage);
|
|
}
|
|
else if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
|
|
{
|
|
nvprintfLevel(level, "GENERAL: %s \n --> %s\n", callbackData->pMessageIdName, callbackData->pMessage);
|
|
}
|
|
else
|
|
{
|
|
nvprintfLevel(level, "%s \n --> %s\n", callbackData->pMessageIdName, callbackData->pMessage);
|
|
}
|
|
|
|
// this seems redundant with the info already in callbackData->pMessage
|
|
#if 0
|
|
|
|
if(callbackData->objectCount > 0)
|
|
{
|
|
for(uint32_t object = 0; object < callbackData->objectCount; ++object)
|
|
{
|
|
std::string otype = ObjectTypeToString(callbackData->pObjects[object].objectType);
|
|
LOGI(" Object[%d] - Type %s, Value %p, Name \"%s\"\n", object, otype.c_str(),
|
|
(void*)(callbackData->pObjects[object].objectHandle), callbackData->pObjects[object].pObjectName);
|
|
}
|
|
}
|
|
if(callbackData->cmdBufLabelCount > 0)
|
|
{
|
|
for(uint32_t label = 0; label < callbackData->cmdBufLabelCount; ++label)
|
|
{
|
|
LOGI(" Label[%d] - %s { %f, %f, %f, %f}\n", label, callbackData->pCmdBufLabels[label].pLabelName,
|
|
callbackData->pCmdBufLabels[label].color[0], callbackData->pCmdBufLabels[label].color[1],
|
|
callbackData->pCmdBufLabels[label].color[2], callbackData->pCmdBufLabels[label].color[3]);
|
|
}
|
|
#endif
|
|
// Don't bail out, but keep going.
|
|
return VK_FALSE;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Create the Vulkan instance and then first compatible device based on \p info
|
|
//
|
|
bool Context::init(const ContextCreateInfo& info)
|
|
{
|
|
if(!initInstance(info))
|
|
return false;
|
|
|
|
// Find all compatible devices
|
|
auto compatibleDevices = getCompatibleDevices(info);
|
|
if(compatibleDevices.empty())
|
|
{
|
|
assert(!"No compatible device found");
|
|
return false;
|
|
}
|
|
|
|
// Use a compatible device
|
|
return initDevice(compatibleDevices[info.compatibleDeviceIndex], info);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Create the Vulkan instance
|
|
//
|
|
bool Context::initInstance(const ContextCreateInfo& info)
|
|
{
|
|
// #Aftermath - Initialization
|
|
if(::isAftermathAvailable() && info.enableAftermath)
|
|
{
|
|
m_gpuCrashTracker.initialize();
|
|
}
|
|
|
|
VkApplicationInfo applicationInfo{VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
|
applicationInfo.pApplicationName = info.appTitle.c_str();
|
|
applicationInfo.pEngineName = info.appEngine.c_str();
|
|
applicationInfo.apiVersion = VK_MAKE_VERSION(info.apiMajor, info.apiMinor, 0);
|
|
|
|
m_apiMajor = info.apiMajor;
|
|
m_apiMinor = info.apiMinor;
|
|
|
|
if(info.verboseUsed)
|
|
{
|
|
uint32_t version;
|
|
VkResult result = vkEnumerateInstanceVersion(&version);
|
|
NVVK_CHECK(result);
|
|
LOGI("_______________\n");
|
|
LOGI("Vulkan Version:\n");
|
|
LOGI(" - available: %d.%d.%d\n", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version));
|
|
LOGI(" - requesting: %d.%d.%d\n", info.apiMajor, info.apiMinor, 0);
|
|
}
|
|
|
|
{
|
|
// Get all layers
|
|
auto layerProperties = getInstanceLayers();
|
|
|
|
if(fillFilteredNameArray(m_usedInstanceLayers, layerProperties, info.instanceLayers) != VK_SUCCESS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(info.verboseAvailable)
|
|
{
|
|
LOGI("___________________________\n");
|
|
LOGI("Available Instance Layers :\n");
|
|
for(auto it : layerProperties)
|
|
{
|
|
LOGI("%s (v. %d.%d.%d %x) : %s\n", it.layerName, VK_VERSION_MAJOR(it.specVersion),
|
|
VK_VERSION_MINOR(it.specVersion), VK_VERSION_PATCH(it.specVersion), it.implementationVersion, it.description);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// Get all extensions
|
|
auto extensionProperties = getInstanceExtensions();
|
|
|
|
std::vector<void*> featureStructs;
|
|
if(fillFilteredNameArray(m_usedInstanceExtensions, extensionProperties, info.instanceExtensions, featureStructs) != VK_SUCCESS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(info.verboseAvailable)
|
|
{
|
|
LOGI("\n");
|
|
LOGI("Available Instance Extensions :\n");
|
|
for(auto it : extensionProperties)
|
|
{
|
|
LOGI("%s (v. %d)\n", it.extensionName, it.specVersion);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(info.verboseUsed)
|
|
{
|
|
LOGI("______________________\n");
|
|
LOGI("Used Instance Layers :\n");
|
|
for(const auto& it : m_usedInstanceLayers)
|
|
{
|
|
LOGI("%s\n", it.c_str());
|
|
}
|
|
LOGI("\n");
|
|
LOGI("Used Instance Extensions :\n");
|
|
for(const auto& it : m_usedInstanceExtensions)
|
|
{
|
|
LOGI("%s\n", it.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
std::vector<const char*> usedInstanceLayers;
|
|
std::vector<const char*> usedInstanceExtensions;
|
|
for(const auto& it : m_usedInstanceExtensions)
|
|
{
|
|
usedInstanceExtensions.push_back(it.c_str());
|
|
}
|
|
for(const auto& it : m_usedInstanceLayers)
|
|
{
|
|
usedInstanceLayers.push_back(it.c_str());
|
|
}
|
|
|
|
VkInstanceCreateInfo instanceCreateInfo{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
|
instanceCreateInfo.pApplicationInfo = &applicationInfo;
|
|
instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(usedInstanceExtensions.size());
|
|
instanceCreateInfo.ppEnabledExtensionNames = usedInstanceExtensions.data();
|
|
instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(usedInstanceLayers.size());
|
|
instanceCreateInfo.ppEnabledLayerNames = usedInstanceLayers.data();
|
|
instanceCreateInfo.pNext = info.instanceCreateInfoExt;
|
|
|
|
NVVK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance));
|
|
|
|
for(const auto& it : usedInstanceExtensions)
|
|
{
|
|
if(strcmp(it, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0)
|
|
{
|
|
initDebugUtils();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Context::initQueueList(QueueScoreList& list, const uint32_t* maxFamilyCounts, const float* priorities, uint32_t maxQueueCount) const
|
|
{
|
|
for(uint32_t qF = 0; qF < m_physicalInfo.queueProperties.size(); ++qF)
|
|
{
|
|
const auto& queueFamily = m_physicalInfo.queueProperties[qF];
|
|
QueueScore score{0, qF, 0, 1.0f};
|
|
|
|
for(uint32_t i = 0; i < 32; i++)
|
|
{
|
|
if(queueFamily.queueFlags & (1 << i))
|
|
{
|
|
score.score++;
|
|
}
|
|
}
|
|
for(uint32_t qI = 0; qI < (maxFamilyCounts ? maxFamilyCounts[qF] : queueFamily.queueCount); ++qI)
|
|
{
|
|
score.queueIndex = qI;
|
|
|
|
if(priorities)
|
|
{
|
|
score.priority = priorities[qF * maxQueueCount + qI];
|
|
}
|
|
|
|
list.emplace_back(score);
|
|
}
|
|
}
|
|
|
|
// Sort the queues for specialization, highest specialization has lowest score
|
|
std::sort(list.begin(), list.end(), [](const QueueScore& lhs, const QueueScore& rhs) {
|
|
if(lhs.score < rhs.score)
|
|
return true;
|
|
if(lhs.score > rhs.score)
|
|
return false;
|
|
if(lhs.priority > rhs.priority)
|
|
return true;
|
|
if(lhs.priority < rhs.priority)
|
|
return false;
|
|
return lhs.queueIndex < rhs.queueIndex;
|
|
});
|
|
}
|
|
|
|
nvvk::Context::QueueScore Context::removeQueueListItem(QueueScoreList& list, VkQueueFlags needFlags, float priority) const
|
|
{
|
|
for(uint32_t q = 0; q < list.size(); ++q)
|
|
{
|
|
const QueueScore& score = list[q];
|
|
auto& family = m_physicalInfo.queueProperties[score.familyIndex];
|
|
if((family.queueFlags & needFlags) == needFlags && score.priority == priority)
|
|
{
|
|
QueueScore queue = score;
|
|
queue.familyIndex = score.familyIndex;
|
|
queue.queueIndex = score.queueIndex;
|
|
// we used this queue
|
|
list.erase(list.begin() + q);
|
|
return queue;
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Create Vulkan device
|
|
// \p deviceIndex is the index from the list of getPhysicalDevices/getPhysicalDeviceGroups
|
|
bool Context::initDevice(uint32_t deviceIndex, const ContextCreateInfo& info)
|
|
{
|
|
assert(m_instance != nullptr);
|
|
|
|
VkPhysicalDeviceGroupProperties physicalGroup;
|
|
if(info.useDeviceGroups)
|
|
{
|
|
auto groups = getPhysicalDeviceGroups();
|
|
assert(deviceIndex < static_cast<uint32_t>(groups.size()));
|
|
physicalGroup = groups[deviceIndex];
|
|
m_physicalDevice = physicalGroup.physicalDevices[0];
|
|
}
|
|
else
|
|
{
|
|
auto physicalDevices = getPhysicalDevices();
|
|
assert(deviceIndex < static_cast<uint32_t>(physicalDevices.size()));
|
|
m_physicalDevice = physicalDevices[deviceIndex];
|
|
}
|
|
|
|
initPhysicalInfo(m_physicalInfo, m_physicalDevice, info.apiMajor, info.apiMinor);
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// queue setup
|
|
|
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
|
std::vector<float> priorities;
|
|
|
|
{
|
|
QueueScoreList queueScoresTemp;
|
|
|
|
uint32_t maxQueueCount = 0;
|
|
|
|
for(auto& it : m_physicalInfo.queueProperties)
|
|
{
|
|
maxQueueCount = std::max(maxQueueCount, it.queueCount);
|
|
}
|
|
// priorities is sized to easily address enough slots for each family
|
|
priorities.resize(m_physicalInfo.queueProperties.size() * maxQueueCount);
|
|
|
|
// init list with all maximum queue counts
|
|
initQueueList(queueScoresTemp, nullptr, nullptr, 0);
|
|
|
|
// figure out how many queues we need per family
|
|
std::vector<uint32_t> queueFamilyCounts(m_physicalInfo.queueProperties.size(), 0);
|
|
|
|
for(auto& it : info.requestedQueues)
|
|
{
|
|
// handle each request individually
|
|
for(uint32_t i = 0; i < it.count; i++)
|
|
{
|
|
// in this pass we don't care about the real priority yet, queueList is initialized with 1.0f
|
|
QueueScore queue = removeQueueListItem(queueScoresTemp, it.requiredFlags, 1.0f);
|
|
if(!queue.score)
|
|
{
|
|
// there were not enough queues left supporting the required flags
|
|
LOGE("could not setup requested queue configuration\n");
|
|
return false;
|
|
}
|
|
|
|
priorities[queue.familyIndex * maxQueueCount + queueFamilyCounts[queue.familyIndex]] = it.priority;
|
|
queueFamilyCounts[queue.familyIndex]++;
|
|
}
|
|
}
|
|
|
|
// init requested families with appropriate family count
|
|
for(uint32_t i = 0; i < m_physicalInfo.queueProperties.size(); ++i)
|
|
{
|
|
if(queueFamilyCounts[i])
|
|
{
|
|
VkDeviceQueueCreateInfo queueInfo{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
|
|
queueInfo.queueFamilyIndex = i;
|
|
queueInfo.queueCount = queueFamilyCounts[i];
|
|
queueInfo.pQueuePriorities = priorities.data() + (i * maxQueueCount);
|
|
|
|
queueCreateInfos.push_back(queueInfo);
|
|
}
|
|
}
|
|
|
|
// setup available queues, now with actual requested counts and available priorities
|
|
initQueueList(m_availableQueues, queueFamilyCounts.data(), priorities.data(), maxQueueCount);
|
|
}
|
|
|
|
VkDeviceCreateInfo deviceCreateInfo{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
|
|
deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size();
|
|
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// version features and physical device extensions
|
|
|
|
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
|
Features11Old features11old;
|
|
std::vector<void*> featureStructs;
|
|
|
|
features2.features = m_physicalInfo.features10;
|
|
features11old.read(m_physicalInfo.features11);
|
|
|
|
if(info.apiMajor == 1 && info.apiMinor == 1)
|
|
{
|
|
features2.pNext = &features11old.multiview;
|
|
}
|
|
|
|
if(info.apiMajor == 1 && info.apiMinor >= 2)
|
|
{
|
|
features2.pNext = &m_physicalInfo.features11;
|
|
m_physicalInfo.features11.pNext = &m_physicalInfo.features12;
|
|
m_physicalInfo.features12.pNext = nullptr;
|
|
}
|
|
|
|
if(info.apiMajor == 1 && info.apiMinor >= 3)
|
|
{
|
|
m_physicalInfo.features12.pNext = &m_physicalInfo.features13;
|
|
m_physicalInfo.features13.pNext = nullptr;
|
|
}
|
|
|
|
auto extensionProperties = getDeviceExtensions(m_physicalDevice);
|
|
|
|
if(info.verboseAvailable)
|
|
{
|
|
LOGI("_____________________________\n");
|
|
LOGI("Available Device Extensions :\n");
|
|
for(auto it : extensionProperties)
|
|
{
|
|
LOGI("%s (v. %d)\n", it.extensionName, it.specVersion);
|
|
}
|
|
}
|
|
|
|
if(fillFilteredNameArray(m_usedDeviceExtensions, extensionProperties, info.deviceExtensions, featureStructs) != VK_SUCCESS)
|
|
{
|
|
deinit();
|
|
return false;
|
|
}
|
|
|
|
if(info.verboseUsed)
|
|
{
|
|
LOGI("________________________\n");
|
|
LOGI("Used Device Extensions :\n");
|
|
for(const auto& it : m_usedDeviceExtensions)
|
|
{
|
|
LOGI("%s\n", it.c_str());
|
|
}
|
|
LOGI("\n");
|
|
}
|
|
|
|
struct ExtensionHeader // Helper struct to link extensions together
|
|
{
|
|
VkStructureType sType;
|
|
void* pNext;
|
|
};
|
|
|
|
// use the features2 chain to append extensions
|
|
if(!featureStructs.empty())
|
|
{
|
|
// build up chain of all used extension features
|
|
for(size_t i = 0; i < featureStructs.size(); i++)
|
|
{
|
|
auto* header = reinterpret_cast<ExtensionHeader*>(featureStructs[i]);
|
|
header->pNext = i < featureStructs.size() - 1 ? featureStructs[i + 1] : nullptr;
|
|
}
|
|
|
|
// append to the end of current feature2 struct chain
|
|
ExtensionHeader* lastCoreFeature = (ExtensionHeader*)&features2;
|
|
while(lastCoreFeature->pNext != nullptr)
|
|
{
|
|
lastCoreFeature = (ExtensionHeader*)lastCoreFeature->pNext;
|
|
}
|
|
lastCoreFeature->pNext = featureStructs[0];
|
|
|
|
// query support
|
|
vkGetPhysicalDeviceFeatures2(m_physicalDevice, &features2);
|
|
}
|
|
|
|
// run user callback to disable features
|
|
if(info.fnDisableFeatures)
|
|
{
|
|
ExtensionHeader* featurePtr = (ExtensionHeader*)&features2;
|
|
while(featurePtr)
|
|
{
|
|
info.fnDisableFeatures(featurePtr->sType, featurePtr);
|
|
featurePtr = (ExtensionHeader*)featurePtr->pNext;
|
|
}
|
|
}
|
|
|
|
// disable this feature through info directly
|
|
if(info.disableRobustBufferAccess)
|
|
{
|
|
features2.features.robustBufferAccess = VK_FALSE;
|
|
}
|
|
|
|
|
|
std::vector<const char*> usedDeviceExtensions;
|
|
for(const auto& it : m_usedDeviceExtensions)
|
|
{
|
|
usedDeviceExtensions.push_back(it.c_str());
|
|
}
|
|
|
|
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(usedDeviceExtensions.size());
|
|
deviceCreateInfo.ppEnabledExtensionNames = usedDeviceExtensions.data();
|
|
|
|
// Vulkan >= 1.1 uses pNext to enable features, and not pEnabledFeatures
|
|
deviceCreateInfo.pEnabledFeatures = nullptr;
|
|
deviceCreateInfo.pNext = &features2;
|
|
|
|
// device group information
|
|
VkDeviceGroupDeviceCreateInfo deviceGroupCreateInfo{VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO};
|
|
if(info.useDeviceGroups)
|
|
{
|
|
// add ourselves to the chain
|
|
deviceGroupCreateInfo.pNext = deviceCreateInfo.pNext;
|
|
deviceGroupCreateInfo.physicalDeviceCount = uint32_t(physicalGroup.physicalDeviceCount);
|
|
deviceGroupCreateInfo.pPhysicalDevices = physicalGroup.physicalDevices;
|
|
deviceCreateInfo.pNext = &deviceGroupCreateInfo;
|
|
}
|
|
|
|
ExtensionHeader* deviceCreateChain = nullptr;
|
|
if(info.deviceCreateInfoExt)
|
|
{
|
|
deviceCreateChain = (ExtensionHeader*)info.deviceCreateInfoExt;
|
|
while(deviceCreateChain->pNext != nullptr)
|
|
{
|
|
deviceCreateChain = (ExtensionHeader*)deviceCreateChain->pNext;
|
|
}
|
|
// override last of external chain
|
|
deviceCreateChain->pNext = (void*)deviceCreateInfo.pNext;
|
|
deviceCreateInfo.pNext = info.deviceCreateInfoExt;
|
|
}
|
|
|
|
VkResult result = vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_device);
|
|
|
|
if(deviceCreateChain)
|
|
{
|
|
// reset last of external chain
|
|
deviceCreateChain->pNext = nullptr;
|
|
}
|
|
|
|
if(result != VK_SUCCESS)
|
|
{
|
|
deinit();
|
|
return false;
|
|
}
|
|
|
|
load_VK_EXTENSIONS(m_instance, vkGetInstanceProcAddr, m_device, vkGetDeviceProcAddr);
|
|
|
|
nvvk::DebugUtil::setEnabled(hasDebugUtils());
|
|
|
|
if(hasDeviceExtension(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME)
|
|
|| hasDeviceExtension(VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME))
|
|
{
|
|
LOGW(
|
|
"\n-------------------------------------------------------------------"
|
|
"\nWARNING: Aftermath extensions enabled. This may affect performance."
|
|
"\n-------------------------------------------------------------------\n\n");
|
|
}
|
|
else if(isAftermathAvailable() && info.enableAftermath)
|
|
{
|
|
LOGW(
|
|
"\n--------------------------------------------------------------"
|
|
"\nWARNING: Attempted to enable Aftermath extensions, but failed."
|
|
"\n" VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME " or\n " VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME
|
|
" not enabled or missing."
|
|
"\n--------------------------------------------------------------\n\n");
|
|
}
|
|
|
|
m_queueGCT = createQueue(info.defaultQueueGCT, "queueGCT", info.defaultPriorityGCT);
|
|
m_queueC = createQueue(info.defaultQueueC, "queueC", info.defaultPriorityC);
|
|
m_queueT = createQueue(info.defaultQueueT, "queueT", info.defaultPriorityT);
|
|
|
|
return true;
|
|
}
|
|
|
|
nvvk::Context::Queue Context::createQueue(VkQueueFlags requiredFlags, const std::string& debugName, float priority)
|
|
{
|
|
if(!requiredFlags || m_availableQueues.empty())
|
|
{
|
|
return Queue();
|
|
}
|
|
|
|
QueueScore score = removeQueueListItem(m_availableQueues, requiredFlags, priority);
|
|
if(!score.score)
|
|
{
|
|
return Queue();
|
|
}
|
|
|
|
nvvk::DebugUtil debugUtil(m_device);
|
|
Queue queue;
|
|
queue.familyIndex = score.familyIndex;
|
|
queue.queueIndex = score.queueIndex;
|
|
queue.priority = score.priority;
|
|
vkGetDeviceQueue(m_device, queue.familyIndex, queue.queueIndex, &queue.queue);
|
|
debugUtil.setObjectName(queue.queue, debugName);
|
|
|
|
return queue;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// returns if GCTQueue supports present \p surface
|
|
//
|
|
bool Context::setGCTQueueWithPresent(VkSurfaceKHR surface)
|
|
{
|
|
VkBool32 supportsPresent;
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, m_queueGCT.familyIndex, surface, &supportsPresent);
|
|
return supportsPresent == VK_TRUE;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Destructor
|
|
//
|
|
void Context::deinit()
|
|
{
|
|
if(m_device)
|
|
{
|
|
VkResult result = vkDeviceWaitIdle(m_device);
|
|
if(nvvk::checkResult(result, __FILE__, __LINE__))
|
|
{
|
|
exit(-1);
|
|
}
|
|
|
|
vkDestroyDevice(m_device, nullptr);
|
|
m_device = VK_NULL_HANDLE;
|
|
}
|
|
if(m_destroyDebugUtilsMessengerEXT)
|
|
{
|
|
// Destroy the Debug Utils Messenger
|
|
m_destroyDebugUtilsMessengerEXT(m_instance, m_dbgMessenger, nullptr);
|
|
}
|
|
|
|
if(m_instance)
|
|
{
|
|
vkDestroyInstance(m_instance, nullptr);
|
|
m_instance = VK_NULL_HANDLE;
|
|
}
|
|
|
|
m_usedInstanceExtensions.clear();
|
|
m_usedInstanceLayers.clear();
|
|
m_usedDeviceExtensions.clear();
|
|
|
|
m_createDebugUtilsMessengerEXT = nullptr;
|
|
m_destroyDebugUtilsMessengerEXT = nullptr;
|
|
m_dbgMessenger = nullptr;
|
|
|
|
nvvk::DebugUtil::setEnabled(false);
|
|
}
|
|
|
|
bool Context::hasDeviceExtension(const char* name) const
|
|
{
|
|
for(const auto& it : m_usedDeviceExtensions)
|
|
{
|
|
if(strcmp(name, it.c_str()) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Context::hasInstanceExtension(const char* name) const
|
|
{
|
|
for(const auto& it : m_usedInstanceExtensions)
|
|
{
|
|
if(strcmp(name, it.c_str()) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//
|
|
ContextCreateInfo::ContextCreateInfo(bool bUseValidation, VkDeviceDiagnosticsConfigFlagsNV aftermathFlags)
|
|
{
|
|
if(defaultQueueGCT)
|
|
{
|
|
requestedQueues.push_back({defaultQueueGCT, 1, defaultPriorityGCT});
|
|
}
|
|
if(defaultQueueT)
|
|
{
|
|
requestedQueues.push_back({defaultQueueT, 1, defaultPriorityT});
|
|
}
|
|
if(defaultQueueC)
|
|
{
|
|
requestedQueues.push_back({defaultQueueC, 1, defaultPriorityC});
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
instanceExtensions.push_back({VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true});
|
|
if(bUseValidation)
|
|
instanceLayers.push_back({"VK_LAYER_KHRONOS_validation", true});
|
|
#endif
|
|
|
|
this->enableAftermath = !!aftermathFlags;
|
|
if(isAftermathAvailable() && this->enableAftermath)
|
|
{
|
|
// #Aftermath
|
|
// Set up device creation info for Aftermath feature flag configuration.
|
|
|
|
// BEWARE: we need to use static here, because addDeviceExtension doesn't make a copy of aftermathInfo.
|
|
// The pointer to aftermathInfo MUST be valid until initDevice() time!!!
|
|
static VkDeviceDiagnosticsConfigCreateInfoNV aftermathInfo{VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV};
|
|
aftermathInfo.flags = aftermathFlags;
|
|
// Enable NV_device_diagnostic_checkpoints extension to be able to use Aftermath event markers.
|
|
addDeviceExtension(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
|
|
// Enable NV_device_diagnostics_config extension to configure Aftermath features.
|
|
addDeviceExtension(VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true, &aftermathInfo);
|
|
}
|
|
}
|
|
|
|
void ContextCreateInfo::addInstanceExtension(const char* name, bool optional)
|
|
{
|
|
instanceExtensions.emplace_back(name, optional);
|
|
}
|
|
|
|
void ContextCreateInfo::addInstanceLayer(const char* name, bool optional)
|
|
{
|
|
instanceLayers.emplace_back(name, optional);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// pFeatureStruct must be provided if a feature structure exists for the given extension.
|
|
// The *pFeatureStruct struct will be filled out by querying the physical device.
|
|
// The filled struct will then be passed to device create info.
|
|
//
|
|
void ContextCreateInfo::addDeviceExtension(const char* name, bool optional, void* pFeatureStruct, uint32_t version)
|
|
{
|
|
deviceExtensions.emplace_back(name, optional, pFeatureStruct, version);
|
|
}
|
|
|
|
void ContextCreateInfo::removeInstanceExtension(const char* name)
|
|
{
|
|
for(size_t i = 0; i < instanceExtensions.size(); i++)
|
|
{
|
|
if(strcmp(instanceExtensions[i].name.c_str(), name) == 0)
|
|
{
|
|
instanceExtensions.erase(instanceExtensions.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContextCreateInfo::removeInstanceLayer(const char* name)
|
|
{
|
|
for(size_t i = 0; i < instanceLayers.size(); i++)
|
|
{
|
|
if(strcmp(instanceLayers[i].name.c_str(), name) == 0)
|
|
{
|
|
instanceLayers.erase(instanceLayers.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContextCreateInfo::removeDeviceExtension(const char* name)
|
|
{
|
|
for(size_t i = 0; i < deviceExtensions.size(); i++)
|
|
{
|
|
if(strcmp(deviceExtensions[i].name.c_str(), name) == 0)
|
|
{
|
|
deviceExtensions.erase(deviceExtensions.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContextCreateInfo::addRequestedQueue(VkQueueFlags flags, uint32_t count /*= 1*/, float priority /*= 1.0f*/)
|
|
{
|
|
requestedQueues.push_back({flags, count, priority});
|
|
}
|
|
|
|
void ContextCreateInfo::setVersion(uint32_t major, uint32_t minor)
|
|
{
|
|
assert(apiMajor == 1 && apiMinor >= 1);
|
|
apiMajor = major;
|
|
apiMinor = minor;
|
|
}
|
|
|
|
VkResult Context::fillFilteredNameArray(std::vector<std::string>& used,
|
|
const std::vector<VkLayerProperties>& properties,
|
|
const ContextCreateInfo::EntryArray& requested)
|
|
{
|
|
for(const auto& itr : requested)
|
|
{
|
|
bool found = false;
|
|
for(const auto& property : properties)
|
|
{
|
|
if(strcmp(itr.name.c_str(), property.layerName) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(found)
|
|
{
|
|
used.push_back(itr.name);
|
|
}
|
|
else if(itr.optional == false)
|
|
{
|
|
LOGE("Requiered layer not found: %s\n", itr.name.c_str());
|
|
return VK_ERROR_LAYER_NOT_PRESENT;
|
|
}
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
} // namespace nvvk
|
|
|
|
|
|
VkResult Context::fillFilteredNameArray(std::vector<std::string>& used,
|
|
const std::vector<VkExtensionProperties>& properties,
|
|
const ContextCreateInfo::EntryArray& requested,
|
|
std::vector<void*>& featureStructs)
|
|
{
|
|
for(const auto& itr : requested)
|
|
{
|
|
bool found = false;
|
|
for(const auto& property : properties)
|
|
{
|
|
if(strcmp(itr.name.c_str(), property.extensionName) == 0 && (itr.version == 0 || itr.version == property.specVersion))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(found)
|
|
{
|
|
used.push_back(itr.name);
|
|
if(itr.pFeatureStruct)
|
|
{
|
|
featureStructs.push_back(itr.pFeatureStruct);
|
|
}
|
|
}
|
|
else if(!itr.optional)
|
|
{
|
|
LOGW("VK_ERROR_EXTENSION_NOT_PRESENT: %s - %d\n", itr.name.c_str(), itr.version);
|
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
}
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void Context::initPhysicalInfo(PhysicalDeviceInfo& info, VkPhysicalDevice physicalDevice, uint32_t versionMajor, uint32_t versionMinor)
|
|
{
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &info.memoryProperties);
|
|
uint32_t count;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &count, nullptr);
|
|
info.queueProperties.resize(count);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &count, info.queueProperties.data());
|
|
|
|
// for queries and device creation
|
|
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
|
VkPhysicalDeviceProperties2 properties2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
|
|
Properties11Old properties11old;
|
|
Features11Old features11old;
|
|
|
|
if(versionMajor == 1 && versionMinor == 1)
|
|
{
|
|
features2.pNext = &features11old.multiview;
|
|
properties2.pNext = &properties11old.maintenance3;
|
|
}
|
|
else if(versionMajor == 1 && versionMinor >= 2)
|
|
{
|
|
features2.pNext = &info.features11;
|
|
info.features11.pNext = &info.features12;
|
|
info.features12.pNext = nullptr;
|
|
|
|
info.properties12.driverID = VK_DRIVER_ID_NVIDIA_PROPRIETARY;
|
|
info.properties12.supportedDepthResolveModes = VK_RESOLVE_MODE_MAX_BIT;
|
|
info.properties12.supportedStencilResolveModes = VK_RESOLVE_MODE_MAX_BIT;
|
|
|
|
properties2.pNext = &info.properties11;
|
|
info.properties11.pNext = &info.properties12;
|
|
info.properties12.pNext = nullptr;
|
|
}
|
|
|
|
if(versionMajor == 1 && versionMinor >= 3)
|
|
{
|
|
info.features12.pNext = &info.features13;
|
|
info.features13.pNext = nullptr;
|
|
info.properties12.pNext = &info.properties13;
|
|
info.properties13.pNext = nullptr;
|
|
}
|
|
|
|
vkGetPhysicalDeviceFeatures2(physicalDevice, &features2);
|
|
vkGetPhysicalDeviceProperties2(physicalDevice, &properties2);
|
|
|
|
info.properties10 = properties2.properties;
|
|
info.features10 = features2.features;
|
|
|
|
if(versionMajor == 1 && versionMinor == 1)
|
|
{
|
|
properties11old.write(info.properties11);
|
|
features11old.write(info.features11);
|
|
}
|
|
}
|
|
|
|
void Context::initDebugUtils()
|
|
{
|
|
// Debug reporting system
|
|
// Setup our pointers to the VK_EXT_debug_utils commands
|
|
m_createDebugUtilsMessengerEXT =
|
|
(PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_instance, "vkCreateDebugUtilsMessengerEXT");
|
|
m_destroyDebugUtilsMessengerEXT =
|
|
(PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_instance, "vkDestroyDebugUtilsMessengerEXT");
|
|
// Create a Debug Utils Messenger that will trigger our callback for any warning
|
|
// or error.
|
|
if(m_createDebugUtilsMessengerEXT != nullptr)
|
|
{
|
|
VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
|
|
dbg_messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT // For debug printf
|
|
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT // GPU info, bug
|
|
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; // Invalid usage
|
|
dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT // Other
|
|
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT // Violation of spec
|
|
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; // Non-optimal use
|
|
dbg_messenger_create_info.pfnUserCallback = debugMessengerCallback;
|
|
dbg_messenger_create_info.pUserData = this;
|
|
NVVK_CHECK(m_createDebugUtilsMessengerEXT(m_instance, &dbg_messenger_create_info, nullptr, &m_dbgMessenger));
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Returns the list of devices or groups compatible with the mandatory extensions
|
|
//
|
|
std::vector<uint32_t> Context::getCompatibleDevices(const ContextCreateInfo& info)
|
|
{
|
|
assert(m_instance != nullptr);
|
|
|
|
std::vector<std::pair<bool, uint32_t>> compatibleDevices;
|
|
std::vector<uint32_t> sortedDevices;
|
|
std::vector<VkPhysicalDeviceGroupProperties> groups;
|
|
std::vector<VkPhysicalDevice> physicalDevices;
|
|
|
|
uint32_t nbElems;
|
|
if(info.useDeviceGroups)
|
|
{
|
|
groups = getPhysicalDeviceGroups();
|
|
nbElems = static_cast<uint32_t>(groups.size());
|
|
}
|
|
else
|
|
{
|
|
physicalDevices = getPhysicalDevices();
|
|
nbElems = static_cast<uint32_t>(physicalDevices.size());
|
|
}
|
|
|
|
if(info.verboseCompatibleDevices)
|
|
{
|
|
LOGI("____________________\n");
|
|
LOGI("Compatible Devices :\n");
|
|
}
|
|
|
|
uint32_t compatible = 0;
|
|
for(uint32_t elemId = 0; elemId < nbElems; elemId++)
|
|
{
|
|
VkPhysicalDevice physicalDevice = info.useDeviceGroups ? groups[elemId].physicalDevices[0] : physicalDevices[elemId];
|
|
|
|
VkPhysicalDeviceProperties props;
|
|
vkGetPhysicalDeviceProperties(physicalDevice, &props);
|
|
|
|
bool discreteGpu = props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
|
|
|
|
// Note: all physical devices in a group are identical
|
|
if(hasMandatoryExtensions(physicalDevice, info, info.verboseCompatibleDevices))
|
|
{
|
|
compatibleDevices.emplace_back(discreteGpu, elemId);
|
|
if(info.verboseCompatibleDevices)
|
|
{
|
|
|
|
LOGI("%d: %s\n", compatible, props.deviceName);
|
|
compatible++;
|
|
}
|
|
}
|
|
else if(info.verboseCompatibleDevices)
|
|
{
|
|
LOGI("Skipping physical device %s\n", props.deviceName);
|
|
}
|
|
}
|
|
if(info.verboseCompatibleDevices)
|
|
{
|
|
LOGI("Physical devices found : ");
|
|
if(compatible > 0)
|
|
{
|
|
LOGI("%d\n", compatible);
|
|
}
|
|
else
|
|
{
|
|
LOGE("OMG... NONE !!\n");
|
|
}
|
|
}
|
|
|
|
// Sorting by discrete GPU
|
|
std::sort(compatibleDevices.begin(), compatibleDevices.end(), [](auto a, auto b) { return a.first > b.first; });
|
|
sortedDevices.reserve(compatibleDevices.size());
|
|
for(const auto& d : compatibleDevices)
|
|
sortedDevices.push_back(d.second);
|
|
|
|
return sortedDevices;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Return true if all extensions in info, marked as required are available on the physicalDevice
|
|
//
|
|
bool Context::hasMandatoryExtensions(VkPhysicalDevice physicalDevice, const ContextCreateInfo& info, bool bVerbose)
|
|
{
|
|
std::vector<VkExtensionProperties> extensionProperties;
|
|
|
|
uint32_t count;
|
|
NVVK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, nullptr));
|
|
extensionProperties.resize(count);
|
|
NVVK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, extensionProperties.data()));
|
|
extensionProperties.resize(std::min(extensionProperties.size(), size_t(count)));
|
|
|
|
return checkEntryArray(extensionProperties, info.deviceExtensions, bVerbose);
|
|
}
|
|
|
|
bool Context::checkEntryArray(const std::vector<VkExtensionProperties>& properties, const ContextCreateInfo::EntryArray& requested, bool bVerbose)
|
|
{
|
|
for(const auto& itr : requested)
|
|
{
|
|
bool found = false;
|
|
for(const auto& property : properties)
|
|
{
|
|
if(strcmp(itr.name.c_str(), property.extensionName) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!found && !itr.optional)
|
|
{
|
|
if(bVerbose)
|
|
{
|
|
LOGW("Could NOT locate mandatory extension '%s'\n", itr.name.c_str());
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::vector<VkPhysicalDevice> Context::getPhysicalDevices()
|
|
{
|
|
uint32_t nbElems;
|
|
std::vector<VkPhysicalDevice> physicalDevices;
|
|
NVVK_CHECK(vkEnumeratePhysicalDevices(m_instance, &nbElems, nullptr));
|
|
physicalDevices.resize(nbElems);
|
|
NVVK_CHECK(vkEnumeratePhysicalDevices(m_instance, &nbElems, physicalDevices.data()));
|
|
return physicalDevices;
|
|
}
|
|
|
|
std::vector<VkPhysicalDeviceGroupProperties> Context::getPhysicalDeviceGroups()
|
|
{
|
|
uint32_t deviceGroupCount;
|
|
std::vector<VkPhysicalDeviceGroupProperties> groups;
|
|
NVVK_CHECK(vkEnumeratePhysicalDeviceGroups(m_instance, &deviceGroupCount, nullptr));
|
|
groups.resize(deviceGroupCount);
|
|
NVVK_CHECK(vkEnumeratePhysicalDeviceGroups(m_instance, &deviceGroupCount, groups.data()));
|
|
return groups;
|
|
}
|
|
|
|
std::vector<VkLayerProperties> Context::getInstanceLayers()
|
|
{
|
|
uint32_t count;
|
|
std::vector<VkLayerProperties> layerProperties;
|
|
NVVK_CHECK(vkEnumerateInstanceLayerProperties(&count, nullptr));
|
|
layerProperties.resize(count);
|
|
NVVK_CHECK(vkEnumerateInstanceLayerProperties(&count, layerProperties.data()));
|
|
layerProperties.resize(std::min(layerProperties.size(), size_t(count)));
|
|
return layerProperties;
|
|
}
|
|
|
|
std::vector<VkExtensionProperties> Context::getInstanceExtensions()
|
|
{
|
|
uint32_t count;
|
|
std::vector<VkExtensionProperties> extensionProperties;
|
|
NVVK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
|
|
extensionProperties.resize(count);
|
|
NVVK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &count, extensionProperties.data()));
|
|
extensionProperties.resize(std::min(extensionProperties.size(), size_t(count)));
|
|
return extensionProperties;
|
|
}
|
|
|
|
std::vector<VkExtensionProperties> Context::getDeviceExtensions(VkPhysicalDevice physicalDevice)
|
|
{
|
|
uint32_t count;
|
|
std::vector<VkExtensionProperties> extensionProperties;
|
|
NVVK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, nullptr));
|
|
extensionProperties.resize(count);
|
|
NVVK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, extensionProperties.data()));
|
|
extensionProperties.resize(std::min(extensionProperties.size(), size_t(count)));
|
|
return extensionProperties;
|
|
}
|
|
|
|
} // namespace nvvk
|
|
|