J'essaie d'apprendre Vulkan en suivant les excellents tutoriels de vulkan-tutorial.com mais j'ai quelques problèmes au point où je dois créer la chaîne d'échange. Comme indiqué dans le titre, vkCreateSwapchainKHR
crée l'erreur suivante: Violation d'accès exécutant l'emplacement 0x0000000000000000
.
Le didacticiel suggère que cela pourrait être un conflit avec la superposition de vapeur. Ce n'est pas le cas pour moi car la copie du code entier du didacticiel fonctionne.
J'essaie de comprendre ce qui n'a pas fonctionné avec mon code et d'apprendre à déboguer des problèmes que je n'aurai pas un code de référence dans le futur. La ligne incriminée ressemble à ceci:
// Useful functions and structures VkPhysicalDevice physicalDevice; VkSurfaceKHR surface; VkSwapchainKHR swapChain; struct QueueFamilyIndices { std::optional<uint32_t> graphicsFamily; std::optional<uint32_t> presentationFamily; bool isComplete() { return graphicsFamily.has_value() && presentationFamily.has_value(); } }; struct SwapChainSupportDetails { VkSurfaceCapabilitiesKHR surfaceCapabilities; std::vector<VkSurfaceFormatKHR> formats; std::vector<VkPresentModeKHR> presentModes; }; SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice physicalDevice) { SwapChainSupportDetails swapChainSupportDetails; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &swapChainSupportDetails.surfaceCapabilities); uint32_t formatCount = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr); if (formatCount != 0) { swapChainSupportDetails.formats.resize(formatCount); vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, swapChainSupportDetails.formats.data()); } uint32_t presentModeCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr); if (presentModeCount != 0) { swapChainSupportDetails.presentModes.resize(presentModeCount); vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, swapChainSupportDetails.presentModes.data()); } return swapChainSupportDetails; } VkSurfaceFormatKHR chooseSwapChainSurfaceFormat(const std::vector<VkSurfaceFormatKHR> & availableFormats) { if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; } for (const auto & availableFormat : availableFormats) { if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { return availableFormat; } } return availableFormats[0]; } VkPresentModeKHR chooseSwapChainPresentMode(const std::vector<VkPresentModeKHR> & availablePresentModes) { VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; for (const auto & availablePresentMode : availablePresentModes) { if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { return availablePresentMode; } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { bestMode = availablePresentMode; } } return bestMode; } VkExtent2D chooseSwapChainExtent2D(const VkSurfaceCapabilitiesKHR & surfaceCapabilities) { if (surfaceCapabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { return surfaceCapabilities.currentExtent; } else { VkExtent2D actualExtent = { WIDTH, HEIGHT }; actualExtent.width = std::max(std::min(surfaceCapabilities.maxImageExtent.width, actualExtent.width), surfaceCapabilities.minImageExtent.width); actualExtent.height = std::max(std::min(surfaceCapabilities.maxImageExtent.height, actualExtent.height), surfaceCapabilities.minImageExtent.height); return actualExtent; } } // Swap Chain creation code SwapChainSupportDetails swapChainSupportDetails = querySwapChainSupport(physicalDevice); VkSurfaceFormatKHR surfaceFormat = chooseSwapChainSurfaceFormat(swapChainSupportDetails.formats); VkPresentModeKHR presentMode = chooseSwapChainPresentMode(swapChainSupportDetails.presentModes); VkExtent2D extent = chooseSwapChainExtent2D(swapChainSupportDetails.surfaceCapabilities); uint32_t imageCount = swapChainSupportDetails.surfaceCapabilities.minImageCount + 1; if (swapChainSupportDetails.surfaceCapabilities.maxImageCount > 0 && imageCount > swapChainSupportDetails.surfaceCapabilities.maxImageCount) { imageCount = swapChainSupportDetails.surfaceCapabilities.minImageCount; } VkSwapchainCreateInfoKHR swapChainCreateInfo = {}; swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapChainCreateInfo.surface = surface; swapChainCreateInfo.minImageCount = imageCount; swapChainCreateInfo.imageFormat = surfaceFormat.format; swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace; swapChainCreateInfo.imageExtent = extent; swapChainCreateInfo.imageArrayLayers = 1; swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; QueueFamilyIndices familyIndices = findQueueFamilies(physicalDevice); uint32_t queueFamilyIndices[] = { familyIndices.graphicsFamily.value(), familyIndices.presentationFamily.value() }; if (familyIndices.graphicsFamily != familyIndices.presentationFamily) { swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; swapChainCreateInfo.queueFamilyIndexCount = 2; swapChainCreateInfo.pQueueFamilyIndices = queueFamilyIndices; } else { swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapChainCreateInfo.queueFamilyIndexCount = 0; swapChainCreateInfo.pQueueFamilyIndices = nullptr; } swapChainCreateInfo.preTransform = swapChainSupportDetails.surfaceCapabilities.currentTransform; swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapChainCreateInfo.presentMode = presentMode; swapChainCreateInfo.clipped = VK_TRUE; swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE; if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("Could not create swap chain"); }
J'ai configuré un point d'arrêt sur cette ligne pour comparer les valeurs des arguments de mon code avec les valeurs du code de référence. Pour autant que je sache, il n'y a pas de différence. (Les adresses sont bien sûr différentes)
Où dois-je rechercher un problème dans mon code? La variable swapChain
est un NULL
comme prévu. Un swapChainCreateInfo
mal formé ne devrait pas faire planter vkCreateSwapchainKHR
. Cela lui ferait simplement renvoyer quelque chose qui n'est pas VK_SUCCESS
. Et l'appareil a été créé sans problème:
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("Failed to create logical device"); }
EDIT - J'utilise la couche de validation VK_LAYER_LUNARG_standard_validation
et ma configuration createInfo
est ce qui suit.
if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("Could not create swap chain"); }
3 Réponses :
Il semble que vous appeliez vkCreateDevice à la fin de votre segment de code pour créer la chaîne d'échange et y passer le VkSwapchainCreateInfo. Peut-être voulez-vous appeler vkCreateSwapchainKHR à la place, comme:
if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain"); }
Si vous appelez réellement vkCreateSwapchainKHR, pourriez-vous modifier votre question pour l'indiquer?
Oui, j'appelle vkCreateSwapchainKHR
, désolé pour cela. J'ai édité le code dans la question :)
Comme @JesseHall l'a suggéré, il est préférable de regarder la sortie des couches de validation pour effectuer le débogage. Pourriez-vous publier le (s) message (s) que vous recevez de la couche de validation?
Je ne reçois aucun message, juste le crash. Je sais que les couches de validation fonctionnent cependant. Avant d'essayer de configurer la chaîne d'échange, j'avais un programme fonctionnel. Dans ce programme, j'ai essayé «d'oublier» de supprimer l'appareil, puis j'ai reçu un message des couches de validation indiquant qu'il y avait une fuite de mémoire. Est-ce que cela a du sens ou est-ce que je manque quelque chose?
est-il possible que le message d'erreur soit pris dans un tampon de sortie et ne soit jamais imprimé parce que le programme plante? Peut-être devriez-vous interrompre le rappel de débogage des couches de validation pour vous assurer que ce n'est pas le cas?
Comme vous pouvez le voir, j'ai résolu mon problème mais je vais certainement essayer ceci!
Non, la couche de validation n'est pas déclenchée: /. Les seuls messages que je vois sont "Messenger ajouté". J'ai essayé de réintroduire mon bogue dans le code de référence et j'obtiens le même résultat. Un crash sans message d'erreur, ce n'est pas cool: /.
Les couches de validation nécessitent l'extension VK_EXT_DEBUG_UTILS_EXTENSION_NAME afin de pouvoir rapporter le message via le rappel que vous enregistrez. Donc, puisque vous n'avez demandé aucune extension, peut-être que les messages n'allaient pas à votre rappel?
Oui définitivement! J'ai essayé d'ajouter uniquement l'extension VK_EXT_DEBUG_UTILS_EXTENSION_NAME et maintenant ma création de surface échoue. J'ai foiré la seule chose qui comptait vraiment haha! Merci de votre aide :)
Plot twist, VK_EXT_DEBUG_UTILS_EXTENSION_NAME est une extension au niveau de l'instance, pas une extension de périphérique logique. Cela signifie qu'il a été activé pour l'instance et que la couche de validation aurait dû intercepter mon erreur: /.
Eh bien, lors de la création du périphérique logique, il faut définir enabledExtensionCount
sur le nombre réel d'extensions requises et non sur 0 si l'on s'attend à ce que les extensions fonctionnent. Dans mon cas, c'était un simple échec d'édition. Voici la gemme dans mon code:
const std::vector<const char*> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
Je l'ai compris en remplaçant chaque fonction de mon code par celles du code de référence jusqu'à ce que cela fonctionne. Je suis un peu déçu que les couches de validation n'aient pas compris cela. Est-ce que je les ai mal réglés? Est-ce quelque chose qu'ils devraient attraper?
EDIT: Comme indiqué par LIANG LIU, voici l'initialisation pour deviceExtensions:
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); createInfo.enabledExtensionCount = 0;
La fonction vkCreateSwapchainKHR () est introduite par l'extension. Si l'extension n'est pas activée lors de la création de l'unité logique, vous ne pouvez pas utiliser cette fonction. C'est donc quelque chose qui devrait certainement être capturé par les couches de validation. Et je suis presque sûr qu'ils découvrent des problèmes comme celui-ci. Le moyen le plus simple de vérifier si les couches de validation fonctionnent correctement est d'activer la couche "api dump" et de voir si les appels de fonction Vulkan sont correctement répertoriés.
Je ne savais pas pour celui-là, merci! Comme l'a souligné Cerulean Quasar dans sa réponse ci-dessous, la couche de validation nécessite une extension elle-même. Donc, en définissant le nombre sur 0, j'ai en fait désactivé la couche de validation.
C'est pourquoi je n'active pas les couches de validation via le code mais via une variable d'environnement qui ne nécessite pas de modifications de code. Toutes les couches de validation ne peuvent probablement pas être activées de cette manière et cette solution n'est pas aussi flexible, mais elle suffit dans la plupart des cas et des erreurs comme celles-ci sont facilement détectées.
Plot twist, VK_EXT_DEBUG_UTILS_EXTENSION_NAME est une extension au niveau de l'instance, pas une extension de périphérique logique. Cela signifie qu'il a été activé pour l'instance et que la couche de validation aurait dû intercepter mon erreur: /. J'ai essayé d'activer la couche de validation VK_LAYER_LUNARG_api_dump et je reçois beaucoup d'informations imprimées sur la console. Cela doit signifier que mes couches de validation fonctionnent. Ils ne semblent tout simplement pas attraper cette erreur spécifique!
Activez VK_KHR_SWAPCHAIN_EXTENSION_NAME lors de la création de VkDevice
void VKRenderer::createVkLogicalDevice() { // device extensions vector<const char*>::type deviceExtensionNames = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; // priorities float queuePrioritys[2] = { 1.f, 1.f}; // graphics queue VkDeviceQueueCreateInfo queueCreateInfos; queueCreateInfos.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfos.pNext = nullptr; queueCreateInfos.queueFamilyIndex = getGraphicsQueueFamilyIndex(); queueCreateInfos.queueCount = 1; queueCreateInfos.pQueuePriorities = &queuePrioritys[0]; // device features VkPhysicalDeviceFeatures deviceFeatures = {}; VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.pQueueCreateInfos = &queueCreateInfos; createInfo.queueCreateInfoCount = 1; createInfo.pEnabledFeatures = &deviceFeatures; createInfo.enabledExtensionCount = deviceExtensionNames.size(); createInfo.ppEnabledExtensionNames = deviceExtensionNames.data(); // create logical device and retrieve graphics queue if (VK_SUCCESS == vkCreateDevice(m_vkPhysicalDevice, &createInfo, nullptr, &m_vkDevice)) { vkGetDeviceQueue(m_vkDevice, getGraphicsQueueFamilyIndex(), 0, &m_vkGraphicsQueue); vkGetDeviceQueue(m_vkDevice, getPresentQueueFamilyIndex(), 0, &m_vkPresentQueue); } else { EchoLogError("Failed to create vulkan logical device!"); } }
Oui, comme vous pouvez le voir dans ma réponse ci-dessus, j'avais fait cela. Malheureusement, j'avais également défini le nombre d'extensions sur 0, ce qui désactivait cette extension. Quelqu'un m'a dit plus tard que cela devrait être capturé par les couches de validation, mais personne n'a ajouté cette vérification spécifique.
Je sais, je réponds à cette question, simplement parce que je ne trouve pas la valeur du "ppEnabledExtensionNames" de votre réponse.
Oh oui, bon appel! J'ai également édité ma réponse et vous ai donné du crédit.
Avez-vous vérifié que le pointeur de fonction
vkCreateSwapchainKHR
a été chargé avec succès par votre chargeur et non parnullptr
?Non, je ne vérifie pas cela dans le code. Je viens juste d'utiliser mon point d'arrêt et l'adresse n'est pas
nullptr
. Merci d'avoir répondu!"Un swapChainCreateInfo mal formé ne devrait pas faire planter vkCreateSwapchainKHR. Cela lui ferait simplement renvoyer quelque chose qui n'est pas VK_SUCCESS." Ce n'est pas vrai. La plupart du temps dans Vulkan, si vous lui donnez une entrée non valide (la spécification définit ce qui est valide), le comportement est indéfini et un comportement non défini peut inclure des plantages. Vous devriez utiliser des couches de validation pour recevoir un message expliquant ce qui n'allait pas avant de planter. Si vous publiez le contenu de VkSwapchainCreateInfo ici, nous pourrons peut-être identifier le problème pour vous.
Merci pour la correction concernant
swapChainCreateInfo
! J'utilise la couche de validationVK_LAYER_LUNARG_standard_validation
mais je ne suis pas sûr de comprendre encore comment elle fonctionne. En outre, j'ai mis à jour le message avec ma configurationVkSwapchainCreateInfo
. Faites-moi savoir si vous avez besoin de plus et merci :)