为什么会出现 "Invalid VkShaderModule Object" 错误?
Why do I get "Invalid VkShaderModule Object" error?
我正在学习 Vulkan vulkan-tutorial.com。
我卡住了,因为我无法弄清楚为什么在创建图形管道时会出现此错误。
VUID-VkPipelineShaderStageCreateInfo-module-parameter(ERROR / SPEC): msgNum: 0 - Invalid VkShaderModule Object 0x70000000007. The Vulkan spec states: module must be a valid VkShaderModule handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-module-parameter)
Objects: 1
[0] 0x70000000007, type: 15, name: NULL
VUID-VkPipelineShaderStageCreateInfo-module-parameter(ERROR / SPEC): msgNum: 0 - Invalid VkShaderModule Object 0x80000000008. The Vulkan spec states: module must be a valid VkShaderModule handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-module-parameter)
Objects: 1
[0] 0x80000000008, type: 15, name: NULL
这就是我创建着色器模块的方式:
auto createShaderModule = [](const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
};
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
auto createStage = [](VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
};
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
createStage(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
createStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
如您所见,我正在检查创建的着色器模块是否正常 (VK_SUCCESS
)。但是验证层仍然说模块无效。
这是完整的代码:
#include <stdio.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
typedef const char* const ConstStr;
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
GLFWwindow* window;
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
struct VulkanData
{
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
u32 graphicsQueueFamily, presentationQueueFamily;
VkQueue graphicsQueue;
VkQueue presentationQueue;
VkSwapchainKHR swapchain;
VkImageView swapchainImageViews[2];
VkRenderPass renderPass;
VkPipeline pipeline;
} vk;
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void initVulkan()
{
{ // create vulkan instance
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = "hello";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "hello_engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pNext = nullptr;
instInfo.flags = 0;
instInfo.pApplicationInfo = &appInfo;
static ConstStr layers[] = {
#if !defined(NDEBUG)
"VK_LAYER_KHRONOS_validation"
#endif
};
instInfo.enabledLayerCount = std::size(layers);
instInfo.ppEnabledLayerNames = layers;
u32 numGlfwExtensions;
ConstStr* glfwExtensions = glfwGetRequiredInstanceExtensions(&instInfo.enabledExtensionCount);
std::vector<const char*> extensions;
extensions.reserve(numGlfwExtensions + 1);
for(u32 i = 0; i < numGlfwExtensions; i++)
extensions.push_back(glfwExtensions[i]);
#ifndef NDEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
instInfo.ppEnabledExtensionNames = glfwGetRequiredInstanceExtensions(&instInfo.enabledExtensionCount);
if (vkCreateInstance(&instInfo, nullptr, &vk.inst) != VK_SUCCESS) {
printf("Error creating vulkan instance\n");
exit(-1);
}
}
// create window surface
if(glfwCreateWindowSurface(vk.inst, window, nullptr, &vk.surface) != VK_SUCCESS) {
printf("Error: can't create window surface\n");
exit(-1);
}
{ // pick physical device
u32 numPhysicalDevices;
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(numPhysicalDevices);
if(numPhysicalDevices == 0) {
printf("Error: there are no devices supporting Vulkan\n");
exit(-1);
}
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, &physicalDevices[0]);
auto compareProps = [](
const VkPhysicalDeviceProperties& propsA,
const VkPhysicalDeviceMemoryProperties& memPropsA,
const VkPhysicalDeviceProperties& propsB,
const VkPhysicalDeviceMemoryProperties& memPropsB) -> bool
{
auto calcDeviceTypeScore = [](VkPhysicalDeviceType a) -> u8 {
switch(a) {
case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
default: return 0;
}
};
const u8 scoreA = calcDeviceTypeScore(propsA.deviceType);
const u8 scoreB = calcDeviceTypeScore(propsB.deviceType);
if(scoreA != scoreB)
return scoreA < scoreB;
auto calcMem = [](const VkPhysicalDeviceMemoryProperties& a) -> u64
{
u64 mem = 0;
for(u32 i = 0; i < a.memoryHeapCount; i++) {
if(a.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
mem = std::max(mem, a.memoryHeaps[i].size);
}
return mem;
};
u32 memA = calcMem(memPropsA);
u32 memB = calcMem(memPropsB);
return memA < memB;
};
VkPhysicalDeviceProperties bestProps;
vkGetPhysicalDeviceProperties(physicalDevices[0], &bestProps);
VkPhysicalDeviceMemoryProperties bestMemProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevices[0], &bestMemProps);
u32 bestI = 0;
for(u32 i = 1; i < numPhysicalDevices; i++) {
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceProperties(physicalDevices[i], &props);
vkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memProps);
if(compareProps(bestProps, bestMemProps, props, memProps)) {
bestProps = props;
bestMemProps = memProps;
bestI = i;
}
}
vk.physicalDevice = physicalDevices[bestI];
}
{ // create logical device
u32 numQueueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProps(numQueueFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, &queueFamilyProps[0]);
vk.graphicsQueueFamily = numQueueFamilies;
vk.presentationQueueFamily = numQueueFamilies;
for(u32 i = 0; i < numQueueFamilies; i++) {
if(queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
vk.graphicsQueueFamily = i;
VkBool32 supportPresentation;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, i, vk.surface, &supportPresentation);
if(supportPresentation)
vk.presentationQueueFamily = i;
}
if(vk.graphicsQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports graphics\n");
exit(-1);
}
if(vk.presentationQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports presentation\n");
exit(-1);
}
u32 queueFamilyInds[2] = {vk.graphicsQueueFamily};
u32 numQueues;
if(vk.graphicsQueueFamily == vk.presentationQueueFamily) {
numQueues = 1;
}
else {
numQueues = 2;
queueFamilyInds[1] = vk.graphicsQueueFamily;
}
const float queuePriorities[] = {1.f};
VkDeviceQueueCreateInfo queueCreateInfos[2] = {};
for(u32 i = 0; i < numQueues; i++)
{
queueCreateInfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[i].queueFamilyIndex = queueFamilyInds[i];
queueCreateInfos[i].queueCount = 1;
queueCreateInfos[i].pQueuePriorities = queuePriorities;
}
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.queueCreateInfoCount = numQueues;
info.pQueueCreateInfos = queueCreateInfos;
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
ConstStr deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
info.enabledExtensionCount = std::size(deviceExtensions);
info.ppEnabledExtensionNames = deviceExtensions;
const VkResult deviceCreatedOk = vkCreateDevice(vk.physicalDevice, &info, nullptr, &vk.device);
if(deviceCreatedOk != VK_SUCCESS) {
printf("Error: couldn't create device\n");
exit(-1);
}
vkGetDeviceQueue(vk.device, vk.graphicsQueueFamily, 0, &vk.graphicsQueue);
vkGetDeviceQueue(vk.device, vk.presentationQueueFamily, 0, &vk.presentationQueue);
// queues
// https://community.khronos.org/t/guidelines-for-selecting-queues-and-families/7222
// https://www.reddit.com/r/vulkan/comments/aara8f/best_way_for_selecting_queuefamilies/
//
}
{ // crete swapchain
VkSurfaceCapabilitiesKHR surfaceCpabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface, &surfaceCpabilities);
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = vk.surface;
createInfo.minImageCount = 2;
createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; // TODO: I think this format has mandatory support but I'm not sure
createInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
createInfo.imageExtent = surfaceCpabilities.currentExtent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &vk.presentationQueueFamily;
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_FALSE;
//createInfo.oldSwapchain = ; // this can be used to recycle the old swapchain when resizing the window
vkCreateSwapchainKHR(vk.device, &createInfo, nullptr, &vk.swapchain);
}
{ // create image views of the swapchain
u32 imageCount;
VkImage images[2];
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, nullptr);
assert(imageCount == 2);
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, images);
for(u32 i = 0; i < 2; i++)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = images[0];
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_B8G8R8A8_SRGB;
// info.components = ; // channel swizzling VK_COMPONENT_SWIZZLE_IDENTITY is 0
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
vkCreateImageView(vk.device, &info, nullptr, &vk.swapchainImageViews[i]);
}
}
}
#define SHADERS_PATH "shaders"
char* readBinFile(int& len, const char* fileName)
{
FILE* file = fopen(fileName, "rb");
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
char* txt = new char[len];
fread(txt, len, 1, file);
fclose(file);
return txt;
}
void createTheGraphicsPipeline()
{
auto createShaderModule = [](const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
};
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
auto createStage = [](VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
};
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
createStage(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
createStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
VkPipelineVertexInputStateCreateInfo vertInputInfo = {};
vertInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertInputInfo.vertexBindingDescriptionCount = 0;
vertInputInfo.pVertexBindingDescriptions = nullptr;
vertInputInfo.vertexAttributeDescriptionCount = 0;
vertInputInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
assemblyInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = WINDOW_WIDTH;
viewport.height = WINDOW_HEIGHT;
viewport.minDepth = 0;
viewport.maxDepth = 1;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {WINDOW_WIDTH, WINDOW_HEIGHT};
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerInfo = {};
rasterizerInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerInfo.depthClampEnable = VK_FALSE; // enabling will clamp depth instead of discarding which can be useful when rendering shadowmaps
rasterizerInfo.rasterizerDiscardEnable = VK_FALSE; // if enable discards all geometry (could be useful for transformfeedback)
rasterizerInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerInfo.lineWidth = 1.f;
rasterizerInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerInfo.depthBiasEnable = VK_FALSE; // useful for shadow mapping
VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; // multisampling: it works by combining the fragment shader results of multiple polygons that rasterize to the same pixel
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = VK_FALSE;
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
//VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_G_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorBlendAttachment;
const VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateInfo.dynamicStateCount = 2;
dynamicStateInfo.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(vk.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
vkDestroyShaderModule(vk.device, fragShadModule, nullptr);
vkDestroyShaderModule(vk.device, vertShadModule, nullptr);
// -- create the renderPass ---
VkAttachmentDescription attachmentDesc = {};
attachmentDesc.format = VK_FORMAT_B8G8R8A8_SRGB;
attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // clear before rendering
attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // store the result after rendering
attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // we don't care. This doesn't guarantee that the contents of th eimage will be preserved, but that's not a problem since we are going to clear it anyways
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // the index in the attachemntDescs array (we only have one)
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // this is an output attachment so we use this enum for best performance
VkSubpassDescription subpassDesc = {};
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorAttachmentRef; // the index of the attachement in this array is referenced in the shader with "layout(location = 0) out vec4 o_color"
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &attachmentDesc;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDesc;
if(vkCreateRenderPass(vk.device, &renderPassInfo, nullptr, &vk.renderPass) != VK_SUCCESS) {
printf("Error creating the renderPass\n");
exit(-1);
}
// -- finally create the pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stagesInfos;
pipelineInfo.pVertexInputState = &vertInputInfo;
pipelineInfo.pInputAssemblyState = &assemblyInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterizerInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlendInfo;
pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = vk.renderPass; // render pass describing the enviroment in which the pipeline will be used
// the pipeline must only be used with a render pass compatilble with this one
pipelineInfo.subpass = 0; // index of the subpass in the render pass where this pipeline will be used
//pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // you can derive from another pipeline
//pipelineInfo.basePipelineIndex = -1;
if(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vk.pipeline) != VK_SUCCESS) {
printf("Error creating the graphics piepeline\n");
exit(-1);
}
}
int main()
{
printf("hello vulkan\n");
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // explicitly tell GLFW not to create an OpenGL context
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "hello vulkan", nullptr, nullptr);
initVulkan();
createTheGraphicsPipeline();
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}
simple.vert.glsl:
#version 450
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
const vec3 colors[3] = vec3[](
vec3(1, 0, 0),
vec3(0, 1, 0),
vec3(0, 0, 1)
);
layout (location = 0)out vec3 v_color;
void main()
{
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
v_color = colors[gl_VertexIndex];
}
simple.frag.glsl
#version 450
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 o_color;
void main() {
o_color = vec4(v_color, 1.0);
}
我终于找到了问题所在:我过早地破坏了着色器模块。看起来您必须在创建管线后让着色器模块保持活动状态。
这是固定码
#include <stdio.h>
#include <string.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
typedef const char* const ConstStr;
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
GLFWwindow* window;
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
struct VulkanData
{
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
u32 graphicsQueueFamily, presentationQueueFamily;
VkQueue graphicsQueue;
VkQueue presentationQueue;
VkSwapchainKHR swapchain;
VkImageView swapchainImageViews[2];
VkRenderPass renderPass;
VkPipeline pipeline;
} vk;
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void initVulkan()
{
{ // create vulkan instance
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = "hello";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "hello_engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
static ConstStr layers[] = {
#if !defined(NDEBUG)
"VK_LAYER_KHRONOS_validation",
//"VK_LAYER_LUNARG_api_dump",
#endif
};
#if !defined(NDEBUG)
u32 numSupportedLayers;
vkEnumerateInstanceLayerProperties(&numSupportedLayers, nullptr);
std::vector<VkLayerProperties> supportedLayers(numSupportedLayers);
vkEnumerateInstanceLayerProperties(&numSupportedLayers, &supportedLayers[0]);
for(ConstStr layer : layers) {
bool supported = false;
for(const auto& supportedLayer : supportedLayers) {
if(strcmp(supportedLayer.layerName, layer) == 0) {
supported = true;
break;
}
}
if(!supported) {
printf("Layer %s is not supported\n", layer);
//assert(false);
}
}
#endif
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pApplicationInfo = &appInfo;
instInfo.enabledLayerCount = std::size(layers);
instInfo.ppEnabledLayerNames = layers;
u32 numGlfwExtensions;
ConstStr* glfwExtensions = glfwGetRequiredInstanceExtensions(&numGlfwExtensions);
std::vector<const char*> extensions;
extensions.reserve(numGlfwExtensions + 1);
for(u32 i = 0; i < numGlfwExtensions; i++)
extensions.push_back(glfwExtensions[i]);
#ifndef NDEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
instInfo.enabledExtensionCount = extensions.size();
instInfo.ppEnabledExtensionNames = extensions.data();
if (vkCreateInstance(&instInfo, nullptr, &vk.inst) != VK_SUCCESS) {
printf("Error creating vulkan instance\n");
exit(-1);
}
}
// create window surface
if(glfwCreateWindowSurface(vk.inst, window, nullptr, &vk.surface) != VK_SUCCESS) {
printf("Error: can't create window surface\n");
exit(-1);
}
{ // pick physical device
u32 numPhysicalDevices;
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(numPhysicalDevices);
if(numPhysicalDevices == 0) {
printf("Error: there are no devices supporting Vulkan\n");
exit(-1);
}
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, &physicalDevices[0]);
auto compareProps = [](
const VkPhysicalDeviceProperties& propsA,
const VkPhysicalDeviceMemoryProperties& memPropsA,
const VkPhysicalDeviceProperties& propsB,
const VkPhysicalDeviceMemoryProperties& memPropsB) -> bool
{
auto calcDeviceTypeScore = [](VkPhysicalDeviceType a) -> u8 {
switch(a) {
case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
default: return 0;
}
};
const u8 scoreA = calcDeviceTypeScore(propsA.deviceType);
const u8 scoreB = calcDeviceTypeScore(propsB.deviceType);
if(scoreA != scoreB)
return scoreA < scoreB;
auto calcMem = [](const VkPhysicalDeviceMemoryProperties& a) -> u64
{
u64 mem = 0;
for(u32 i = 0; i < a.memoryHeapCount; i++) {
if(a.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
mem = std::max(mem, a.memoryHeaps[i].size);
}
return mem;
};
u32 memA = calcMem(memPropsA);
u32 memB = calcMem(memPropsB);
return memA < memB;
};
VkPhysicalDeviceProperties bestProps;
vkGetPhysicalDeviceProperties(physicalDevices[0], &bestProps);
VkPhysicalDeviceMemoryProperties bestMemProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevices[0], &bestMemProps);
u32 bestI = 0;
for(u32 i = 1; i < numPhysicalDevices; i++) {
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceProperties(physicalDevices[i], &props);
vkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memProps);
if(compareProps(bestProps, bestMemProps, props, memProps)) {
bestProps = props;
bestMemProps = memProps;
bestI = i;
}
}
vk.physicalDevice = physicalDevices[bestI];
}
{ // create logical device
u32 numQueueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProps(numQueueFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, &queueFamilyProps[0]);
vk.graphicsQueueFamily = numQueueFamilies;
vk.presentationQueueFamily = numQueueFamilies;
for(u32 i = 0; i < numQueueFamilies; i++) {
if(queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
vk.graphicsQueueFamily = i;
VkBool32 supportPresentation;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, i, vk.surface, &supportPresentation);
if(supportPresentation)
vk.presentationQueueFamily = i;
}
if(vk.graphicsQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports graphics\n");
exit(-1);
}
if(vk.presentationQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports presentation\n");
exit(-1);
}
u32 queueFamilyInds[2] = {vk.graphicsQueueFamily};
u32 numQueues;
if(vk.graphicsQueueFamily == vk.presentationQueueFamily) {
numQueues = 1;
}
else {
numQueues = 2;
queueFamilyInds[1] = vk.graphicsQueueFamily;
}
const float queuePriorities[] = {1.f};
VkDeviceQueueCreateInfo queueCreateInfos[2] = {};
for(u32 i = 0; i < numQueues; i++)
{
queueCreateInfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[i].queueFamilyIndex = queueFamilyInds[i];
queueCreateInfos[i].queueCount = 1;
queueCreateInfos[i].pQueuePriorities = queuePriorities;
}
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.queueCreateInfoCount = numQueues;
info.pQueueCreateInfos = queueCreateInfos;
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
ConstStr deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
info.enabledExtensionCount = std::size(deviceExtensions);
info.ppEnabledExtensionNames = deviceExtensions;
const VkResult deviceCreatedOk = vkCreateDevice(vk.physicalDevice, &info, nullptr, &vk.device);
if(deviceCreatedOk != VK_SUCCESS) {
printf("Error: couldn't create device\n");
exit(-1);
}
vkGetDeviceQueue(vk.device, vk.graphicsQueueFamily, 0, &vk.graphicsQueue);
vkGetDeviceQueue(vk.device, vk.presentationQueueFamily, 0, &vk.presentationQueue);
// queues
// https://community.khronos.org/t/guidelines-for-selecting-queues-and-families/7222
// https://www.reddit.com/r/vulkan/comments/aara8f/best_way_for_selecting_queuefamilies/
//
}
{ // crete swapchain
VkSurfaceCapabilitiesKHR surfaceCpabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface, &surfaceCpabilities);
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = vk.surface;
createInfo.minImageCount = 2;
createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; // TODO: I think this format has mandatory support but I'm not sure
createInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
createInfo.imageExtent = surfaceCpabilities.currentExtent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &vk.presentationQueueFamily;
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_FALSE;
//createInfo.oldSwapchain = ; // this can be used to recycle the old swapchain when resizing the window
vkCreateSwapchainKHR(vk.device, &createInfo, nullptr, &vk.swapchain);
}
{ // create image views of the swapchain
u32 imageCount;
VkImage images[2];
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, nullptr);
assert(imageCount == 2);
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, images);
for(u32 i = 0; i < 2; i++)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = images[0];
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_B8G8R8A8_SRGB;
// info.components = ; // channel swizzling VK_COMPONENT_SWIZZLE_IDENTITY is 0
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
vkCreateImageView(vk.device, &info, nullptr, &vk.swapchainImageViews[i]);
}
}
}
#define SHADERS_PATH "shaders"
char* readBinFile(int& len, const char* fileName)
{
FILE* file = fopen(fileName, "rb");
if(!file)
return nullptr;
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
char* txt = new char[len];
fread(txt, len, 1, file);
fclose(file);
return txt;
}
VkShaderModule createShaderModule(const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
if(!data) {
printf("Error loading shader spir-v\n");
exit(-1);
}
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
}
VkPipelineShaderStageCreateInfo makeStageCreateInfo(VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
}
void createTheGraphicsPipeline()
{
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
makeStageCreateInfo(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
makeStageCreateInfo(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
VkPipelineVertexInputStateCreateInfo vertInputInfo = {};
vertInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertInputInfo.vertexBindingDescriptionCount = 0;
vertInputInfo.pVertexBindingDescriptions = nullptr;
vertInputInfo.vertexAttributeDescriptionCount = 0;
vertInputInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
assemblyInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = WINDOW_WIDTH;
viewport.height = WINDOW_HEIGHT;
viewport.minDepth = 0;
viewport.maxDepth = 1;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {WINDOW_WIDTH, WINDOW_HEIGHT};
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerInfo = {};
rasterizerInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerInfo.depthClampEnable = VK_FALSE; // enabling will clamp depth instead of discarding which can be useful when rendering shadowmaps
rasterizerInfo.rasterizerDiscardEnable = VK_FALSE; // if enable discards all geometry (could be useful for transformfeedback)
rasterizerInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerInfo.lineWidth = 1.f;
rasterizerInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerInfo.depthBiasEnable = VK_FALSE; // useful for shadow mapping
VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; // multisampling: it works by combining the fragment shader results of multiple polygons that rasterize to the same pixel
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = VK_FALSE;
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
//VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_G_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
//colorBlendInfo.logicOpEnable = VK_FALSE;
//colorBlendInfo.logicOp = VK_LOGIC_OP_COPY;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorBlendAttachment;
//const VkDynamicState dynamicStates[] = {
// VK_DYNAMIC_STATE_VIEWPORT,
// VK_DYNAMIC_STATE_SCISSOR
//};
//VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
//dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
//dynamicStateInfo.dynamicStateCount = 2;
//dynamicStateInfo.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(vk.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
// -- create the renderPass ---
VkAttachmentDescription attachmentDesc = {};
attachmentDesc.format = VK_FORMAT_B8G8R8A8_SRGB;
attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // clear before rendering
attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // store the result after rendering
attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // we don't care. This doesn't guarantee that the contents of th eimage will be preserved, but that's not a problem since we are going to clear it anyways
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // the index in the attachemntDescs array (we only have one)
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // this is an output attachment so we use this enum for best performance
VkSubpassDescription subpassDesc = {};
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorAttachmentRef; // the index of the attachement in this array is referenced in the shader with "layout(location = 0) out vec4 o_color"
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &attachmentDesc;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDesc;
if(vkCreateRenderPass(vk.device, &renderPassInfo, nullptr, &vk.renderPass) != VK_SUCCESS) {
printf("Error creating the renderPass\n");
exit(-1);
}
// -- finally create the pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stagesInfos;
pipelineInfo.pVertexInputState = &vertInputInfo;
pipelineInfo.pInputAssemblyState = &assemblyInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterizerInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlendInfo;
//pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = vk.renderPass; // render pass describing the enviroment in which the pipeline will be used
// the pipeline must only be used with a render pass compatilble with this one
pipelineInfo.subpass = 0; // index of the subpass in the render pass where this pipeline will be used
//pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // you can derive from another pipeline
//pipelineInfo.basePipelineIndex = -1;
getchar();
if(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vk.pipeline) != VK_SUCCESS) {
printf("Error creating the graphics piepeline\n");
exit(-1);
}
vkDestroyShaderModule(vk.device, fragShadModule, nullptr);
vkDestroyShaderModule(vk.device, vertShadModule, nullptr);
}
int main()
{
printf("hello vulkan\n");
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // explicitly tell GLFW not to create an OpenGL context
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "hello vulkan", nullptr, nullptr);
initVulkan();
createTheGraphicsPipeline();
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}
我正在学习 Vulkan vulkan-tutorial.com。
我卡住了,因为我无法弄清楚为什么在创建图形管道时会出现此错误。
VUID-VkPipelineShaderStageCreateInfo-module-parameter(ERROR / SPEC): msgNum: 0 - Invalid VkShaderModule Object 0x70000000007. The Vulkan spec states: module must be a valid VkShaderModule handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-module-parameter)
Objects: 1
[0] 0x70000000007, type: 15, name: NULL
VUID-VkPipelineShaderStageCreateInfo-module-parameter(ERROR / SPEC): msgNum: 0 - Invalid VkShaderModule Object 0x80000000008. The Vulkan spec states: module must be a valid VkShaderModule handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-module-parameter)
Objects: 1
[0] 0x80000000008, type: 15, name: NULL
这就是我创建着色器模块的方式:
auto createShaderModule = [](const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
};
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
auto createStage = [](VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
};
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
createStage(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
createStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
如您所见,我正在检查创建的着色器模块是否正常 (VK_SUCCESS
)。但是验证层仍然说模块无效。
这是完整的代码:
#include <stdio.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
typedef const char* const ConstStr;
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
GLFWwindow* window;
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
struct VulkanData
{
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
u32 graphicsQueueFamily, presentationQueueFamily;
VkQueue graphicsQueue;
VkQueue presentationQueue;
VkSwapchainKHR swapchain;
VkImageView swapchainImageViews[2];
VkRenderPass renderPass;
VkPipeline pipeline;
} vk;
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void initVulkan()
{
{ // create vulkan instance
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = "hello";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "hello_engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pNext = nullptr;
instInfo.flags = 0;
instInfo.pApplicationInfo = &appInfo;
static ConstStr layers[] = {
#if !defined(NDEBUG)
"VK_LAYER_KHRONOS_validation"
#endif
};
instInfo.enabledLayerCount = std::size(layers);
instInfo.ppEnabledLayerNames = layers;
u32 numGlfwExtensions;
ConstStr* glfwExtensions = glfwGetRequiredInstanceExtensions(&instInfo.enabledExtensionCount);
std::vector<const char*> extensions;
extensions.reserve(numGlfwExtensions + 1);
for(u32 i = 0; i < numGlfwExtensions; i++)
extensions.push_back(glfwExtensions[i]);
#ifndef NDEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
instInfo.ppEnabledExtensionNames = glfwGetRequiredInstanceExtensions(&instInfo.enabledExtensionCount);
if (vkCreateInstance(&instInfo, nullptr, &vk.inst) != VK_SUCCESS) {
printf("Error creating vulkan instance\n");
exit(-1);
}
}
// create window surface
if(glfwCreateWindowSurface(vk.inst, window, nullptr, &vk.surface) != VK_SUCCESS) {
printf("Error: can't create window surface\n");
exit(-1);
}
{ // pick physical device
u32 numPhysicalDevices;
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(numPhysicalDevices);
if(numPhysicalDevices == 0) {
printf("Error: there are no devices supporting Vulkan\n");
exit(-1);
}
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, &physicalDevices[0]);
auto compareProps = [](
const VkPhysicalDeviceProperties& propsA,
const VkPhysicalDeviceMemoryProperties& memPropsA,
const VkPhysicalDeviceProperties& propsB,
const VkPhysicalDeviceMemoryProperties& memPropsB) -> bool
{
auto calcDeviceTypeScore = [](VkPhysicalDeviceType a) -> u8 {
switch(a) {
case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
default: return 0;
}
};
const u8 scoreA = calcDeviceTypeScore(propsA.deviceType);
const u8 scoreB = calcDeviceTypeScore(propsB.deviceType);
if(scoreA != scoreB)
return scoreA < scoreB;
auto calcMem = [](const VkPhysicalDeviceMemoryProperties& a) -> u64
{
u64 mem = 0;
for(u32 i = 0; i < a.memoryHeapCount; i++) {
if(a.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
mem = std::max(mem, a.memoryHeaps[i].size);
}
return mem;
};
u32 memA = calcMem(memPropsA);
u32 memB = calcMem(memPropsB);
return memA < memB;
};
VkPhysicalDeviceProperties bestProps;
vkGetPhysicalDeviceProperties(physicalDevices[0], &bestProps);
VkPhysicalDeviceMemoryProperties bestMemProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevices[0], &bestMemProps);
u32 bestI = 0;
for(u32 i = 1; i < numPhysicalDevices; i++) {
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceProperties(physicalDevices[i], &props);
vkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memProps);
if(compareProps(bestProps, bestMemProps, props, memProps)) {
bestProps = props;
bestMemProps = memProps;
bestI = i;
}
}
vk.physicalDevice = physicalDevices[bestI];
}
{ // create logical device
u32 numQueueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProps(numQueueFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, &queueFamilyProps[0]);
vk.graphicsQueueFamily = numQueueFamilies;
vk.presentationQueueFamily = numQueueFamilies;
for(u32 i = 0; i < numQueueFamilies; i++) {
if(queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
vk.graphicsQueueFamily = i;
VkBool32 supportPresentation;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, i, vk.surface, &supportPresentation);
if(supportPresentation)
vk.presentationQueueFamily = i;
}
if(vk.graphicsQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports graphics\n");
exit(-1);
}
if(vk.presentationQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports presentation\n");
exit(-1);
}
u32 queueFamilyInds[2] = {vk.graphicsQueueFamily};
u32 numQueues;
if(vk.graphicsQueueFamily == vk.presentationQueueFamily) {
numQueues = 1;
}
else {
numQueues = 2;
queueFamilyInds[1] = vk.graphicsQueueFamily;
}
const float queuePriorities[] = {1.f};
VkDeviceQueueCreateInfo queueCreateInfos[2] = {};
for(u32 i = 0; i < numQueues; i++)
{
queueCreateInfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[i].queueFamilyIndex = queueFamilyInds[i];
queueCreateInfos[i].queueCount = 1;
queueCreateInfos[i].pQueuePriorities = queuePriorities;
}
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.queueCreateInfoCount = numQueues;
info.pQueueCreateInfos = queueCreateInfos;
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
ConstStr deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
info.enabledExtensionCount = std::size(deviceExtensions);
info.ppEnabledExtensionNames = deviceExtensions;
const VkResult deviceCreatedOk = vkCreateDevice(vk.physicalDevice, &info, nullptr, &vk.device);
if(deviceCreatedOk != VK_SUCCESS) {
printf("Error: couldn't create device\n");
exit(-1);
}
vkGetDeviceQueue(vk.device, vk.graphicsQueueFamily, 0, &vk.graphicsQueue);
vkGetDeviceQueue(vk.device, vk.presentationQueueFamily, 0, &vk.presentationQueue);
// queues
// https://community.khronos.org/t/guidelines-for-selecting-queues-and-families/7222
// https://www.reddit.com/r/vulkan/comments/aara8f/best_way_for_selecting_queuefamilies/
//
}
{ // crete swapchain
VkSurfaceCapabilitiesKHR surfaceCpabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface, &surfaceCpabilities);
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = vk.surface;
createInfo.minImageCount = 2;
createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; // TODO: I think this format has mandatory support but I'm not sure
createInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
createInfo.imageExtent = surfaceCpabilities.currentExtent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &vk.presentationQueueFamily;
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_FALSE;
//createInfo.oldSwapchain = ; // this can be used to recycle the old swapchain when resizing the window
vkCreateSwapchainKHR(vk.device, &createInfo, nullptr, &vk.swapchain);
}
{ // create image views of the swapchain
u32 imageCount;
VkImage images[2];
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, nullptr);
assert(imageCount == 2);
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, images);
for(u32 i = 0; i < 2; i++)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = images[0];
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_B8G8R8A8_SRGB;
// info.components = ; // channel swizzling VK_COMPONENT_SWIZZLE_IDENTITY is 0
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
vkCreateImageView(vk.device, &info, nullptr, &vk.swapchainImageViews[i]);
}
}
}
#define SHADERS_PATH "shaders"
char* readBinFile(int& len, const char* fileName)
{
FILE* file = fopen(fileName, "rb");
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
char* txt = new char[len];
fread(txt, len, 1, file);
fclose(file);
return txt;
}
void createTheGraphicsPipeline()
{
auto createShaderModule = [](const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
};
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
auto createStage = [](VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
};
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
createStage(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
createStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
VkPipelineVertexInputStateCreateInfo vertInputInfo = {};
vertInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertInputInfo.vertexBindingDescriptionCount = 0;
vertInputInfo.pVertexBindingDescriptions = nullptr;
vertInputInfo.vertexAttributeDescriptionCount = 0;
vertInputInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
assemblyInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = WINDOW_WIDTH;
viewport.height = WINDOW_HEIGHT;
viewport.minDepth = 0;
viewport.maxDepth = 1;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {WINDOW_WIDTH, WINDOW_HEIGHT};
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerInfo = {};
rasterizerInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerInfo.depthClampEnable = VK_FALSE; // enabling will clamp depth instead of discarding which can be useful when rendering shadowmaps
rasterizerInfo.rasterizerDiscardEnable = VK_FALSE; // if enable discards all geometry (could be useful for transformfeedback)
rasterizerInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerInfo.lineWidth = 1.f;
rasterizerInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerInfo.depthBiasEnable = VK_FALSE; // useful for shadow mapping
VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; // multisampling: it works by combining the fragment shader results of multiple polygons that rasterize to the same pixel
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = VK_FALSE;
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
//VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_G_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorBlendAttachment;
const VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateInfo.dynamicStateCount = 2;
dynamicStateInfo.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(vk.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
vkDestroyShaderModule(vk.device, fragShadModule, nullptr);
vkDestroyShaderModule(vk.device, vertShadModule, nullptr);
// -- create the renderPass ---
VkAttachmentDescription attachmentDesc = {};
attachmentDesc.format = VK_FORMAT_B8G8R8A8_SRGB;
attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // clear before rendering
attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // store the result after rendering
attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // we don't care. This doesn't guarantee that the contents of th eimage will be preserved, but that's not a problem since we are going to clear it anyways
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // the index in the attachemntDescs array (we only have one)
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // this is an output attachment so we use this enum for best performance
VkSubpassDescription subpassDesc = {};
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorAttachmentRef; // the index of the attachement in this array is referenced in the shader with "layout(location = 0) out vec4 o_color"
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &attachmentDesc;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDesc;
if(vkCreateRenderPass(vk.device, &renderPassInfo, nullptr, &vk.renderPass) != VK_SUCCESS) {
printf("Error creating the renderPass\n");
exit(-1);
}
// -- finally create the pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stagesInfos;
pipelineInfo.pVertexInputState = &vertInputInfo;
pipelineInfo.pInputAssemblyState = &assemblyInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterizerInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlendInfo;
pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = vk.renderPass; // render pass describing the enviroment in which the pipeline will be used
// the pipeline must only be used with a render pass compatilble with this one
pipelineInfo.subpass = 0; // index of the subpass in the render pass where this pipeline will be used
//pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // you can derive from another pipeline
//pipelineInfo.basePipelineIndex = -1;
if(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vk.pipeline) != VK_SUCCESS) {
printf("Error creating the graphics piepeline\n");
exit(-1);
}
}
int main()
{
printf("hello vulkan\n");
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // explicitly tell GLFW not to create an OpenGL context
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "hello vulkan", nullptr, nullptr);
initVulkan();
createTheGraphicsPipeline();
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}
simple.vert.glsl:
#version 450
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
const vec3 colors[3] = vec3[](
vec3(1, 0, 0),
vec3(0, 1, 0),
vec3(0, 0, 1)
);
layout (location = 0)out vec3 v_color;
void main()
{
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
v_color = colors[gl_VertexIndex];
}
simple.frag.glsl
#version 450
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 o_color;
void main() {
o_color = vec4(v_color, 1.0);
}
我终于找到了问题所在:我过早地破坏了着色器模块。看起来您必须在创建管线后让着色器模块保持活动状态。
这是固定码
#include <stdio.h>
#include <string.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
typedef const char* const ConstStr;
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
GLFWwindow* window;
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
struct VulkanData
{
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
u32 graphicsQueueFamily, presentationQueueFamily;
VkQueue graphicsQueue;
VkQueue presentationQueue;
VkSwapchainKHR swapchain;
VkImageView swapchainImageViews[2];
VkRenderPass renderPass;
VkPipeline pipeline;
} vk;
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
void initVulkan()
{
{ // create vulkan instance
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = "hello";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "hello_engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
static ConstStr layers[] = {
#if !defined(NDEBUG)
"VK_LAYER_KHRONOS_validation",
//"VK_LAYER_LUNARG_api_dump",
#endif
};
#if !defined(NDEBUG)
u32 numSupportedLayers;
vkEnumerateInstanceLayerProperties(&numSupportedLayers, nullptr);
std::vector<VkLayerProperties> supportedLayers(numSupportedLayers);
vkEnumerateInstanceLayerProperties(&numSupportedLayers, &supportedLayers[0]);
for(ConstStr layer : layers) {
bool supported = false;
for(const auto& supportedLayer : supportedLayers) {
if(strcmp(supportedLayer.layerName, layer) == 0) {
supported = true;
break;
}
}
if(!supported) {
printf("Layer %s is not supported\n", layer);
//assert(false);
}
}
#endif
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pApplicationInfo = &appInfo;
instInfo.enabledLayerCount = std::size(layers);
instInfo.ppEnabledLayerNames = layers;
u32 numGlfwExtensions;
ConstStr* glfwExtensions = glfwGetRequiredInstanceExtensions(&numGlfwExtensions);
std::vector<const char*> extensions;
extensions.reserve(numGlfwExtensions + 1);
for(u32 i = 0; i < numGlfwExtensions; i++)
extensions.push_back(glfwExtensions[i]);
#ifndef NDEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
instInfo.enabledExtensionCount = extensions.size();
instInfo.ppEnabledExtensionNames = extensions.data();
if (vkCreateInstance(&instInfo, nullptr, &vk.inst) != VK_SUCCESS) {
printf("Error creating vulkan instance\n");
exit(-1);
}
}
// create window surface
if(glfwCreateWindowSurface(vk.inst, window, nullptr, &vk.surface) != VK_SUCCESS) {
printf("Error: can't create window surface\n");
exit(-1);
}
{ // pick physical device
u32 numPhysicalDevices;
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(numPhysicalDevices);
if(numPhysicalDevices == 0) {
printf("Error: there are no devices supporting Vulkan\n");
exit(-1);
}
vkEnumeratePhysicalDevices(vk.inst, &numPhysicalDevices, &physicalDevices[0]);
auto compareProps = [](
const VkPhysicalDeviceProperties& propsA,
const VkPhysicalDeviceMemoryProperties& memPropsA,
const VkPhysicalDeviceProperties& propsB,
const VkPhysicalDeviceMemoryProperties& memPropsB) -> bool
{
auto calcDeviceTypeScore = [](VkPhysicalDeviceType a) -> u8 {
switch(a) {
case VK_PHYSICAL_DEVICE_TYPE_CPU: return 1;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 2;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 4;
default: return 0;
}
};
const u8 scoreA = calcDeviceTypeScore(propsA.deviceType);
const u8 scoreB = calcDeviceTypeScore(propsB.deviceType);
if(scoreA != scoreB)
return scoreA < scoreB;
auto calcMem = [](const VkPhysicalDeviceMemoryProperties& a) -> u64
{
u64 mem = 0;
for(u32 i = 0; i < a.memoryHeapCount; i++) {
if(a.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
mem = std::max(mem, a.memoryHeaps[i].size);
}
return mem;
};
u32 memA = calcMem(memPropsA);
u32 memB = calcMem(memPropsB);
return memA < memB;
};
VkPhysicalDeviceProperties bestProps;
vkGetPhysicalDeviceProperties(physicalDevices[0], &bestProps);
VkPhysicalDeviceMemoryProperties bestMemProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevices[0], &bestMemProps);
u32 bestI = 0;
for(u32 i = 1; i < numPhysicalDevices; i++) {
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceProperties(physicalDevices[i], &props);
vkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memProps);
if(compareProps(bestProps, bestMemProps, props, memProps)) {
bestProps = props;
bestMemProps = memProps;
bestI = i;
}
}
vk.physicalDevice = physicalDevices[bestI];
}
{ // create logical device
u32 numQueueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProps(numQueueFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice, &numQueueFamilies, &queueFamilyProps[0]);
vk.graphicsQueueFamily = numQueueFamilies;
vk.presentationQueueFamily = numQueueFamilies;
for(u32 i = 0; i < numQueueFamilies; i++) {
if(queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
vk.graphicsQueueFamily = i;
VkBool32 supportPresentation;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, i, vk.surface, &supportPresentation);
if(supportPresentation)
vk.presentationQueueFamily = i;
}
if(vk.graphicsQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports graphics\n");
exit(-1);
}
if(vk.presentationQueueFamily == numQueueFamilies) {
printf("Error: there is no queue that supports presentation\n");
exit(-1);
}
u32 queueFamilyInds[2] = {vk.graphicsQueueFamily};
u32 numQueues;
if(vk.graphicsQueueFamily == vk.presentationQueueFamily) {
numQueues = 1;
}
else {
numQueues = 2;
queueFamilyInds[1] = vk.graphicsQueueFamily;
}
const float queuePriorities[] = {1.f};
VkDeviceQueueCreateInfo queueCreateInfos[2] = {};
for(u32 i = 0; i < numQueues; i++)
{
queueCreateInfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfos[i].queueFamilyIndex = queueFamilyInds[i];
queueCreateInfos[i].queueCount = 1;
queueCreateInfos[i].pQueuePriorities = queuePriorities;
}
VkDeviceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.queueCreateInfoCount = numQueues;
info.pQueueCreateInfos = queueCreateInfos;
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
ConstStr deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
info.enabledExtensionCount = std::size(deviceExtensions);
info.ppEnabledExtensionNames = deviceExtensions;
const VkResult deviceCreatedOk = vkCreateDevice(vk.physicalDevice, &info, nullptr, &vk.device);
if(deviceCreatedOk != VK_SUCCESS) {
printf("Error: couldn't create device\n");
exit(-1);
}
vkGetDeviceQueue(vk.device, vk.graphicsQueueFamily, 0, &vk.graphicsQueue);
vkGetDeviceQueue(vk.device, vk.presentationQueueFamily, 0, &vk.presentationQueue);
// queues
// https://community.khronos.org/t/guidelines-for-selecting-queues-and-families/7222
// https://www.reddit.com/r/vulkan/comments/aara8f/best_way_for_selecting_queuefamilies/
//
}
{ // crete swapchain
VkSurfaceCapabilitiesKHR surfaceCpabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface, &surfaceCpabilities);
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = vk.surface;
createInfo.minImageCount = 2;
createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; // TODO: I think this format has mandatory support but I'm not sure
createInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
createInfo.imageExtent = surfaceCpabilities.currentExtent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &vk.presentationQueueFamily;
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_FALSE;
//createInfo.oldSwapchain = ; // this can be used to recycle the old swapchain when resizing the window
vkCreateSwapchainKHR(vk.device, &createInfo, nullptr, &vk.swapchain);
}
{ // create image views of the swapchain
u32 imageCount;
VkImage images[2];
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, nullptr);
assert(imageCount == 2);
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, images);
for(u32 i = 0; i < 2; i++)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = images[0];
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_B8G8R8A8_SRGB;
// info.components = ; // channel swizzling VK_COMPONENT_SWIZZLE_IDENTITY is 0
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
vkCreateImageView(vk.device, &info, nullptr, &vk.swapchainImageViews[i]);
}
}
}
#define SHADERS_PATH "shaders"
char* readBinFile(int& len, const char* fileName)
{
FILE* file = fopen(fileName, "rb");
if(!file)
return nullptr;
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
char* txt = new char[len];
fread(txt, len, 1, file);
fclose(file);
return txt;
}
VkShaderModule createShaderModule(const char* fileName)
{
int len;
char* data = readBinFile(len, fileName);
if(!data) {
printf("Error loading shader spir-v\n");
exit(-1);
}
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.codeSize = len;
info.pCode = (u32*)data;
VkShaderModule module;
const VkResult res = vkCreateShaderModule(vk.device, &info, nullptr, &module);
if(res != VK_SUCCESS) {
printf("Error: could not create vertex shader module\n");
exit(-1);
}
delete[] data;
return module;
}
VkPipelineShaderStageCreateInfo makeStageCreateInfo(VkShaderStageFlagBits stage, VkShaderModule module)
{
VkPipelineShaderStageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage = stage;
info.module = module;
info.pName = "main";
info.pSpecializationInfo = nullptr; // allows to specify values for shader constants
return info;
}
void createTheGraphicsPipeline()
{
VkShaderModule vertShadModule = createShaderModule(SHADERS_PATH"/simple.vert.glsl.spv");
VkShaderModule fragShadModule = createShaderModule(SHADERS_PATH"/simple.frag.glsl.spv");
const VkPipelineShaderStageCreateInfo stagesInfos[] = {
makeStageCreateInfo(VK_SHADER_STAGE_VERTEX_BIT, vertShadModule),
makeStageCreateInfo(VK_SHADER_STAGE_FRAGMENT_BIT, fragShadModule)
};
VkPipelineVertexInputStateCreateInfo vertInputInfo = {};
vertInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertInputInfo.vertexBindingDescriptionCount = 0;
vertInputInfo.pVertexBindingDescriptions = nullptr;
vertInputInfo.vertexAttributeDescriptionCount = 0;
vertInputInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
assemblyInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = WINDOW_WIDTH;
viewport.height = WINDOW_HEIGHT;
viewport.minDepth = 0;
viewport.maxDepth = 1;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {WINDOW_WIDTH, WINDOW_HEIGHT};
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerInfo = {};
rasterizerInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerInfo.depthClampEnable = VK_FALSE; // enabling will clamp depth instead of discarding which can be useful when rendering shadowmaps
rasterizerInfo.rasterizerDiscardEnable = VK_FALSE; // if enable discards all geometry (could be useful for transformfeedback)
rasterizerInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerInfo.lineWidth = 1.f;
rasterizerInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerInfo.depthBiasEnable = VK_FALSE; // useful for shadow mapping
VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; // multisampling: it works by combining the fragment shader results of multiple polygons that rasterize to the same pixel
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = VK_FALSE;
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
//VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_G_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
//colorBlendInfo.logicOpEnable = VK_FALSE;
//colorBlendInfo.logicOp = VK_LOGIC_OP_COPY;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorBlendAttachment;
//const VkDynamicState dynamicStates[] = {
// VK_DYNAMIC_STATE_VIEWPORT,
// VK_DYNAMIC_STATE_SCISSOR
//};
//VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
//dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
//dynamicStateInfo.dynamicStateCount = 2;
//dynamicStateInfo.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(vk.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
// -- create the renderPass ---
VkAttachmentDescription attachmentDesc = {};
attachmentDesc.format = VK_FORMAT_B8G8R8A8_SRGB;
attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // clear before rendering
attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // store the result after rendering
attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // we don't care. This doesn't guarantee that the contents of th eimage will be preserved, but that's not a problem since we are going to clear it anyways
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // the index in the attachemntDescs array (we only have one)
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // this is an output attachment so we use this enum for best performance
VkSubpassDescription subpassDesc = {};
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorAttachmentRef; // the index of the attachement in this array is referenced in the shader with "layout(location = 0) out vec4 o_color"
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &attachmentDesc;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDesc;
if(vkCreateRenderPass(vk.device, &renderPassInfo, nullptr, &vk.renderPass) != VK_SUCCESS) {
printf("Error creating the renderPass\n");
exit(-1);
}
// -- finally create the pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = stagesInfos;
pipelineInfo.pVertexInputState = &vertInputInfo;
pipelineInfo.pInputAssemblyState = &assemblyInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterizerInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlendInfo;
//pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = vk.renderPass; // render pass describing the enviroment in which the pipeline will be used
// the pipeline must only be used with a render pass compatilble with this one
pipelineInfo.subpass = 0; // index of the subpass in the render pass where this pipeline will be used
//pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // you can derive from another pipeline
//pipelineInfo.basePipelineIndex = -1;
getchar();
if(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vk.pipeline) != VK_SUCCESS) {
printf("Error creating the graphics piepeline\n");
exit(-1);
}
vkDestroyShaderModule(vk.device, fragShadModule, nullptr);
vkDestroyShaderModule(vk.device, vertShadModule, nullptr);
}
int main()
{
printf("hello vulkan\n");
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // explicitly tell GLFW not to create an OpenGL context
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "hello vulkan", nullptr, nullptr);
initVulkan();
createTheGraphicsPipeline();
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}