4
社区成员
发帖
与我相关
我的任务
分享在 VS2022 中配置 Vulkan、GLFW 和 GLM 主要分为三步:先下载好这三个库,然后在 VS 项目里指定头文件和库的路径,最后在代码中包含头文件并链接库文件。
最关键的步骤在于正确配置项目的附加包含目录、附加库目录和附加依赖项,下面一步步来操作。
首先,你需要下载运行代码所需的 Vulkan SDK、GLFW 和 GLM 库。
安装 Vulkan SDK
前往 LunarG Vulkan SDK 官网 下载最新版本的 SDK。
下载后是一个安装程序,建议使用默认安装路径(如 C:\VulkanSDK\),安装时建议勾选所有组件以避免后续缺少文件。
安装完成后,进入 Vulkan SDK 的 Bin 目录(如 C:\VulkanSDK\<版本号>\Bin),双击运行 vkcube.exe。如果出现一个旋转的彩色立方体,说明你的显卡驱动和 SDK 都安装成功了。
下载 GLFW 库 (负责创建窗口)
前往 GLFW 官网下载页,在 "Windows pre-compiled binaries" 部分,下载 64-bit Windows binaries。
下载后解压到你选定的目录(如 D:\Libs\glfw-3.4.bin.WIN64)。你会看到 include 和 lib-vc2022 这两个关键文件夹。
下载 GLM 库 (负责数学运算)
前往 GLM Github 仓库 下载最新的源码包(如 glm-0.9.9.8.zip 或更新版本)。
下载后解压到你选定的目录(如 D:\Libs\glm),你只需要用到里面的 glm 文件夹。
现在,我们将在你的 VS2022 项目里把这些库连接起来。
创建项目:在 VS2022 中新建一个 C++ 空项目。你可以在项目创建向导中搜索"空项目"来找到这个模板。
打开属性页:在右侧"解决方案资源管理器"中,右键点击你的项目名称,选择属性。
小提示:为了方便切换,建议先在属性页窗口顶部的"配置"下拉菜单中,选择所有配置,"平台"选择所有平台。
配置头文件目录:在左侧菜单栏,导航至 C/C++ -> 常规,在右侧找到附加包含目录进行编辑。点击出现的小文件夹图标,然后添加以下三个路径:
Vulkan 头文件目录:C:\VulkanSDK\<你的版本号>\Include
GLFW 头文件目录:你的GLFW解压路径\include
GLM 头文件目录:你的GLM解压路径\glm
配置库文件目录:在左侧菜单栏,导航至链接器 -> 常规,在右侧找到附加库目录进行编辑。同样点击图标,添加以下两个路径:
Vulkan 库文件目录:C:\VulkanSDK\<你的版本号>\Lib
GLFW 库文件目录:你的GLFW解压路径\lib-vc2022
指定要链接的库文件:在左侧菜单栏,导航至链接器 -> 输入,在右侧找到附加依赖项进行编辑。在弹出的对话框中,逐行添加以下两个库文件名:
vulkan-1.lib
glfw3.lib
(可选) 设置C++语言标准:为了兼容性,建议在C/C++ -> 语言中,将C++ 语言标准设置为 ISO C++17 标准 (/std:c++17)。
完成以上所有步骤后,点击"确定"保存你的项目属性设置。
现在我们来编写一段测试代码,验证环境是否配置成功。
创建源文件:在"解决方案资源管理器"中,右键点击项目下的"源文件"文件夹,选择"添加" -> "新建项",创建一个名为 main.cpp 的文件。
编写测试代码:将下面的代码复制并粘贴到你的 main.cpp 文件中,覆盖原有内容。这段代码会初始化 GLFW 和 Vulkan,并打印支持的扩展数量。
cpp
// 必须在包含glfw3.h之前定义这个宏,它会自动包含vulkan.h
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
// GLM 相关宏和头文件
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <iostream>
int main() {
// 1. 初始化 GLFW
glfwInit();
// 告诉 GLFW 不要创建 OpenGL 上下文,因为我们要用 Vulkan
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
// 创建一个窗口
GLFWwindow* window = glfwCreateWindow(800, 600, "Vulkan 测试窗口", nullptr, nullptr);
// 2. 测试 Vulkan 基本功能
uint32_t extensionCount = 0;
// 枚举所有支持的 Vulkan 实例扩展
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::cout << "此驱动支持 " << extensionCount << " 个 Vulkan 扩展。\n";
// 3. 测试 GLM 数学库 (一个简单的向量矩阵乘法)
glm::mat4 matrix;
glm::vec4 vec;
auto test = matrix * vec;
std::cout << "GLM 数学库工作正常。\n";
// 4. 进入主循环,持续监听窗口事件,直到用户关闭窗口
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
// 5. 清理资源并退出
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
编译运行:点击 VS 顶部菜单栏的 本地 Windows 调试器 运行程序。
如果一切顺利,你会看到一个名为 "Vulkan 测试窗口" 的空白窗口弹出,同时在 VS 的输出或控制台窗口中,会打印出支持的 Vulkan 扩展数量。
根据不同的配置情况和需求,可以参考下表:
| 遇到的问题 | 可能的原因与解决办法 |
|---|---|
找不到 glfw3.h 或 vulkan.h | 这是最常见的错误。说明附加包含目录配置不正确或路径没写对。请回到第二步的第3小步,仔细检查你添加的三个路径是否正确指向了 GLFW 和 glm 文件夹。 |
链接错误 LNK2019 (无法解析的外部符号) | 这说明链接器找不到函数的具体实现。请检查: 1. 附加库目录中是否正确添加了 lib-vc2022 和 Vulkan Lib 文件夹。2. 附加依赖项中是否准确添加了 vulkan-1.lib 和 glfw3.lib。 |
| 程序运行后弹窗很快消失 | 这通常是程序逻辑问题,但为了看清输出,你可以在 main 函数的 return 0; 前加上 system("pause"); 这行代码。 |
当你完成基础环境的配置后,如果想进一步学习Vulkan的各种渲染技巧,最佳实践是直接研究Khronos Group官方维护的示例项目。
它包含了从基础入门到高级特性的大量完整代码。你可以通过以下方式获取并构建它:
bash
git clone --recurse-submodules https://github.com/KhronosGroup/Vulkan-Samples.git cd Vulkan-Samples cmake -G "Visual Studio 17 2022" -A x64 -S . -B build/windows cmake --build build/windows --config Release --target vulkan_samples
构建成功后,你就可以在 build/windows/app/bin/Release/AMD64/ 目录下找到 vulkan_samples.exe,并在VS中通过设置命令行参数(如 sample hello_triangle)来运行具体的示例。
绘制三角形
你的项目文件夹/
├── main.cpp
└── shaders/
├── triangle.vert
└── triangle.frag
代码
#version 450
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
}
#version 450
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(0.0, 0.5, 0.8, 1.0);
}
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
const int WIDTH = 800;
const int HEIGHT = 600;
// 读取 SPIR-V 文件
std::vector<char> readFile(const std::string& filename) {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("Failed to open file: " + filename);
}
size_t fileSize = (size_t)file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
int main() {
try {
// 读取编译好的着色器
std::cout << "Loading shaders..." << std::endl;
auto vertCode = readFile("shaders/vert.spv");
auto fragCode = readFile("shaders/frag.spv");
std::cout << "Vertex shader size: " << vertCode.size() << " bytes" << std::endl;
std::cout << "Fragment shader size: " << fragCode.size() << " bytes" << std::endl;
// 初始化 GLFW
if (!glfwInit()) {
throw std::runtime_error("GLFW init failed");
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Triangle", nullptr, nullptr);
if (!window) {
throw std::runtime_error("Window creation failed");
}
// 创建 Vulkan 实例
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Triangle";
appInfo.apiVersion = VK_API_VERSION_1_0;
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.enabledExtensionCount = (uint32_t)extensions.size();
createInfo.ppEnabledExtensionNames = extensions.data();
VkInstance instance;
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("Instance creation failed");
}
// 创建 Surface
VkSurfaceKHR surface;
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("Surface creation failed");
}
// 选择 GPU
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0) {
throw std::runtime_error("No Vulkan GPU found");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
VkPhysicalDevice physicalDevice = devices[0];
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physicalDevice, &props);
std::cout << "Using GPU: " << props.deviceName << std::endl;
// 找队列族
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
uint32_t graphicsFamily = UINT32_MAX;
for (uint32_t i = 0; i < queueFamilyCount; i++) {
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphicsFamily = i;
break;
}
}
if (graphicsFamily == UINT32_MAX) {
throw std::runtime_error("No graphics queue");
}
// 创建设备
float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = graphicsFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
const char* deviceExtension = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
VkDeviceCreateInfo deviceCreateInfo{};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
deviceCreateInfo.enabledExtensionCount = 1;
deviceCreateInfo.ppEnabledExtensionNames = &deviceExtension;
VkDevice device;
if (vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device) != VK_SUCCESS) {
throw std::runtime_error("Device creation failed");
}
VkQueue graphicsQueue;
vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue);
// 创建 SwapChain
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &capabilities);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
std::vector<VkSurfaceFormatKHR> formats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, formats.data());
VkSurfaceFormatKHR surfaceFormat = formats[0];
VkExtent2D extent = capabilities.currentExtent;
if (extent.width == 0xFFFFFFFF) {
extent.width = WIDTH;
extent.height = HEIGHT;
}
VkSwapchainCreateInfoKHR swapCreateInfo{};
swapCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapCreateInfo.surface = surface;
swapCreateInfo.minImageCount = 2;
swapCreateInfo.imageFormat = surfaceFormat.format;
swapCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
swapCreateInfo.imageExtent = extent;
swapCreateInfo.imageArrayLayers = 1;
swapCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapCreateInfo.preTransform = capabilities.currentTransform;
swapCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapCreateInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
swapCreateInfo.clipped = VK_TRUE;
VkSwapchainKHR swapChain;
if (vkCreateSwapchainKHR(device, &swapCreateInfo, nullptr, &swapChain) != VK_SUCCESS) {
throw std::runtime_error("Swapchain creation failed");
}
uint32_t imageCount;
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
std::vector<VkImage> swapChainImages(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
// 创建 ImageView
std::vector<VkImageView> swapChainImageViews(imageCount);
for (uint32_t i = 0; i < imageCount; i++) {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = swapChainImages[i];
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = surfaceFormat.format;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &viewInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
throw std::runtime_error("ImageView creation failed");
}
}
// 创建 RenderPass
VkAttachmentDescription colorAttachment{};
colorAttachment.format = surfaceFormat.format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
VkRenderPass renderPass;
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
throw std::runtime_error("RenderPass creation failed");
}
// 创建 Shader Module
VkShaderModuleCreateInfo vertModuleInfo{};
vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vertModuleInfo.codeSize = vertCode.size();
vertModuleInfo.pCode = reinterpret_cast<const uint32_t*>(vertCode.data());
VkShaderModule vertShaderModule;
if (vkCreateShaderModule(device, &vertModuleInfo, nullptr, &vertShaderModule) != VK_SUCCESS) {
throw std::runtime_error("Vertex shader module creation failed");
}
VkShaderModuleCreateInfo fragModuleInfo{};
fragModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
fragModuleInfo.codeSize = fragCode.size();
fragModuleInfo.pCode = reinterpret_cast<const uint32_t*>(fragCode.data());
VkShaderModule fragShaderModule;
if (vkCreateShaderModule(device, &fragModuleInfo, nullptr, &fragShaderModule) != VK_SUCCESS) {
throw std::runtime_error("Fragment shader module creation failed");
}
VkPipelineShaderStageCreateInfo vertStage{};
vertStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertStage.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertStage.module = vertShaderModule;
vertStage.pName = "main";
VkPipelineShaderStageCreateInfo fragStage{};
fragStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragStage.module = fragShaderModule;
fragStage.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = { vertStage, fragStage };
// 创建 Pipeline
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)extent.width;
viewport.height = (float)extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = extent;
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
VkPipelineLayout pipelineLayout;
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("Pipeline layout creation failed");
}
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
VkPipeline graphicsPipeline;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
throw std::runtime_error("Graphics pipeline creation failed");
}
// 创建 Framebuffer
std::vector<VkFramebuffer> framebuffers(imageCount);
for (uint32_t i = 0; i < imageCount; i++) {
VkImageView attachments[] = { swapChainImageViews[i] };
VkFramebufferCreateInfo fbInfo{};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = renderPass;
fbInfo.attachmentCount = 1;
fbInfo.pAttachments = attachments;
fbInfo.width = extent.width;
fbInfo.height = extent.height;
fbInfo.layers = 1;
if (vkCreateFramebuffer(device, &fbInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("Framebuffer creation failed");
}
}
// 创建 Command Pool 和 Command Buffers
VkCommandPool commandPool;
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = graphicsFamily;
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
throw std::runtime_error("Command pool creation failed");
}
std::vector<VkCommandBuffer> commandBuffers(imageCount);
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = imageCount;
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
throw std::runtime_error("Command buffer allocation failed");
}
for (uint32_t i = 0; i < imageCount; i++) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
vkBeginCommandBuffer(commandBuffers[i], &beginInfo);
VkRenderPassBeginInfo rpBegin{};
rpBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBegin.renderPass = renderPass;
rpBegin.framebuffer = framebuffers[i];
rpBegin.renderArea.extent = extent;
VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
rpBegin.clearValueCount = 1;
rpBegin.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffers[i], &rpBegin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(commandBuffers[i]);
vkEndCommandBuffer(commandBuffers[i]);
}
// 同步对象
VkSemaphore semaphore;
VkFence fence;
VkSemaphoreCreateInfo semInfo{};
semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
vkCreateSemaphore(device, &semInfo, nullptr, &semaphore);
vkCreateFence(device, &fenceInfo, nullptr, &fence);
std::cout << "Entering main loop - Triangle should appear!" << std::endl;
std::cout << "Press ESC to exit" << std::endl;
// 主循环
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
vkResetFences(device, 1, &fence);
uint32_t imageIndex;
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, semaphore, VK_NULL_HANDLE, &imageIndex);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &semaphore;
VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
submitInfo.pWaitDstStageMask = &waitStage;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &semaphore;
vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence);
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &semaphore;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapChain;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR(graphicsQueue, &presentInfo);
}
// 清理
vkDeviceWaitIdle(device);
vkDestroySemaphore(device, semaphore, nullptr);
vkDestroyFence(device, fence, nullptr);
vkDestroyCommandPool(device, commandPool, nullptr);
for (auto fb : framebuffers) vkDestroyFramebuffer(device, fb, nullptr);
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
vkDestroyShaderModule(device, fragShaderModule, nullptr);
for (auto iv : swapChainImageViews) vkDestroyImageView(device, iv, nullptr);
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
std::cout << "Done!" << std::endl;
}
catch (const std::exception& e) {
std::cerr << "ERROR: " << e.what() << std::endl;
std::cin.get();
return -1;
}
return 0;
}
编译
既然你已经有了 .vert 和 .frag 文件,先用命令行工具把它们编译成 .spv 文件:
打开命令提示符(不是 VS 的)
进入你的项目目录
运行:
cmd
glslc shaders/triangle.vert -o shaders/vert.spv glslc shaders/triangle.frag -o shaders/frag.spv
然后使用这个简单版本的 main.cpp: