上一天的地址:/z736248591/article/details/11796
————————————哥是可爱的分割线————————————————
第二天 5月24日
回顾:上一天创建了项目,决定了目标和工具。今天继续。
这里使用GLFW作为渲染库。
GLFW介绍:
GLFWis an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan development on the desktop. It provides a simple API for creating windows, contexts and surfaces, receiving input and events.
GLFW is written in C and supports Windows, macOS, X11 and Wayland.
GLFW is licensed under the zlib/libpng license.
下载glfw丢到external,这里使用OpenGL。
CmakeLists.txt:
# CMakeList.txt: TheSeedGameEngine 的 CMake 项目,在此处包括源代码并定义# 项目特定的逻辑。#cmake_minimum_required (VERSION 3.8)project ("TheSeedGameEngine"VERSION 0.0.1DESCRIPTION "A 2D multi-platform game engine"HOMEPAGE_URL "/nayaku/TheSeedGameEngine"LANGUAGES C CXX)# 添加glwf库set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)add_subdirectory(external/glfw-3.3.4)# 找到OpenGLfind_package(OpenGL REQUIRED)# 将源代码添加到此项目的可执行文件。add_executable (TheSeedGameEnginesrc/main.c)target_link_libraries(TheSeedGameEngine glfwOpenGL::GL)# TODO: 如有需要,请添加测试并安装目标。
我们在src\main.c填入代码:
#include<GLFW/glfw3.h>int main(){GLFWwindow* window;// 初始化GLFW库if (!glfwInit())return -1;// 创建窗口和OpenGL的内容window = glfwCreateWindow(640, 480, "The Seed Game Engine", NULL, NULL);if (!window){glfwTerminate();return -1;}// 创建内容glfwMakeContextCurrent(window);// 主循环while (!glfwWindowShouldClose(window)){// 渲染glClear(GL_COLOR_BUFFER_BIT);// 交换缓冲glfwSwapBuffers(window);// 处理事件消息glfwPollEvents();}glfwTerminate();return 0;}
编译运行:
很不错!成功了。
额外补充:win10以后在命令行输入tree可以查看文件树,不需要安装额外的软件。
开始编写渲染部分
新建一个Vulkan实例。
static VkInstance vulkanInstance;static void VulkanInitInstance(){// 应用信息VkApplicationInfo appInfo = {.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,.pApplicationName = "Demo",.applicationVersion = VK_MAKE_VERSION(1,0,0),.pEngineName = "The Seed Game Engine",.engineVersion = VK_MAKE_VERSION(1,0,0),.apiVersion = VK_API_VERSION_1_0,};// 使用glfw扩展unsigned int glfwExtensionCount = 0;const char** glfwExtensions;glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);// 实例信息VkInstanceCreateInfo createInfo = {.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,.pApplicationInfo = &appInfo,.enabledExtensionCount = glfwExtensionCount,.ppEnabledExtensionNames = glfwExtensions,.enabledLayerCount = 0};// 创建实例VkResult result = vkCreateInstance(&createInfo,NULL,&vulkanInstance);if(result == VK_SUCCESS){printf("Vulkan实例创建成功!\n");}else{printf("Vulkan实例创建失败!\n");abort();}}
销毁实例:
static void VulkanDestroyInstance(){vkDestroyInstance(vulkanInstance,NULL);printf("Vulkan实例销毁完毕");glfwDestroyWindow(window);glfwTerminate();}
初始化窗口
void InitWindow(int width,int height,const char* title){glfwInit();glfwWindowHint(GLFW_CLIENT_API,GLFW_NO_API);glfwWindowHint(GLFW_RESIZABLE,GLFW_FALSE);window = glfwCreateWindow(width,height,title,NULL,NULL);}
这里封装一下glfwWindowShouldClose
函数
int WindowShouldClose(){int flag = glfwWindowShouldClose(window);if(!flag){glfwPollEvents();}return flag;}
修改main函数
int main(){InitWindow(800,600,"Demo");while (!WindowShouldClose()){}CloseWindow();return 0;}
运行:
成功运行。
继续。。。
添加获取物理设备
static void PickPhysicalDevice(){uint32_t deviceCount;VkResult result = vkEnumeratePhysicalDevices(instance, &deviceCount, NULL);assert(result == VK_SUCCESS);if (deviceCount == 0){printf("Failed to find GPUs with Vulkan support!");abort();}VkPhysicalDevice* devices = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * deviceCount);vkEnumeratePhysicalDevices(instance, &deviceCount, devices);// 选择可用的物理设备VkPhysicalDevice physicalDevice=devices[0];//没啥特殊要求,直接选择第一个设备即可free(devices);// 输出选择的物理设备信息VkPhysicalDeviceProperties deviceProperties;vkGetPhysicalDeviceProperties(device, &deviceProperties);printf("Current Physical Info:ID: %I32u\nName: %s\nVulkan Version: %I32u\n",deviceProperties.deviceID,deviceProperties.deviceName,deviceProperties.apiVersion);}
接下去要判断可用的队列族。这里一直没搞懂什么是队列族,今天来查查看。
不同的queue有着不同的职能,有的负责普通的3D图形渲染的例如Graphic Queue,有的负责像素块Blit的例如Transfer Queue,有的是负责计算的例如Compute Queue,还有负责稀疏绑定的例如Sparse Binding。当然有的queue能同时负责多个职能的,一般第一个是全能的1。其中队列族支持的功能,用queueFlags表示2。
我的集成显卡是Intel HDU Graphics630,就只有一个队列。可以图形、计算、传输、稀疏矩阵。
(注:该软件为GPU-Z 官网 汉化版下载)
独立显卡是GTX 1050,一共有3个队列家族。
补充PickPhysicalDevice
函数完整
// 获取设备队列族的数量uint32_t queueFamilyCount;vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL);VkQueueFamilyProperties* queueFamilyProperties = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * queueFamilyCount);vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL);// 遍历队列族for (uint32_t i = 0; i < queueFamilyCount; i++){// 支持图形工作if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT){queueFamilyIndex = i;break;}}free(queueFamilyProperties);
然后在c文件的头添加queueFamilyIndex
定义。
static uint32_t queueFamilyIndex = -1;
运行后输出如下:
Current Vulkan instance created success.Physical Info:ID: 7308Name: GeForce GTX 1050 TiVulkan Version: 4202651Vulkan instance destroyed.
继续。。。
编写CreateLogicalDevice()
函数。
这里只创建一个队列。因为很多时候没有必要创建多个队列。这是因为可以在多个线程上创建所有命令缓冲区,然后在主线程一次性的以较低开销的调用提交队列3。
float queuePriority = 1.0f;VkDeviceQueueCreateInfo queueCreateInfo={.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,.queueFamilyIndex = queueFamilyIndex,.queueCount = 1,// 只需要创建一个队列.pQueuePriorities = &queuePriority}
这里需要VK_KHR_SWAPCHAIN_EXTENSION_NAME扩展支持,因为并不是所有的图形卡具备能力将绘制的图像直接显示到屏幕上4。
验证层开启比较麻烦,而且有些情况下也不支持。这里先不开。
const char *deviceExtensionNames[]={VK_KHR_SWAPCHAIN_EXTENSION_NAME};VkDeviceCreateInfo createInfo = {.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,.pQueueCreateInfos = &queueCreateInfo,.queueCreateInfoCount = 1,.ppEnabledExtensionNames = deviceExtensionNames,.enabledLayerCount = 0 // 先不开启验证层};
最后创建逻辑设备
VkResult result = vkCreateDevice(physicalDevice,&createInfo,NULL,&logicalDevice);
今天到这里就结束了,也写了好长一堆代码,最后贴出完整的VulkanManager.c
的全部代码。完整的可以去Github上
#include "VulkanManager.h"#include <stdio.h>#include <stdlib.h>#include <assert.h>// GLFW窗口static GLFWwindow* window;// Vulkan实例static VkInstance instance;// 物理设备static VkPhysicalDevice physicalDevice;// 使用的队列家族编号static uint32_t queueFamilyIndex = -1;// 逻辑设备static VkDevice logicalDevice;/* 初始化实例 */static void InitInstance(const char* title){// 应用信息VkApplicationInfo appInfo = {.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,.pApplicationName = title,.applicationVersion = VK_MAKE_VERSION(1, 0, 0),.pEngineName = "The Seed Game Engine",.engineVersion = VK_MAKE_VERSION(1, 0, 0),.apiVersion = VK_API_VERSION_1_0,};// 使用glfw扩展unsigned int glfwExtensionCount = 0;const char** glfwExtensions;glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);// 实例信息VkInstanceCreateInfo createInfo = {.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,.pApplicationInfo = &appInfo,.enabledExtensionCount = glfwExtensionCount,.ppEnabledExtensionNames = glfwExtensions,.enabledLayerCount = 0};// 创建实例VkResult result = vkCreateInstance(&createInfo, NULL, &instance);if (result == VK_SUCCESS){printf("Vulkan instance created success.\n");}else{printf("Vulkan instance created failed.\n");abort();}}/* 销毁实例 */static void DestroyInstance(){vkDestroyInstance(instance, NULL);printf("Vulkan instance destroyed.");glfwDestroyWindow(window);}/* 获取物理设备 */static void PickPhysicalDevice(){uint32_t deviceCount;VkResult result = vkEnumeratePhysicalDevices(instance, &deviceCount, NULL);assert(result == VK_SUCCESS);if (deviceCount == 0){printf("Failed to find GPUs with Vulkan support!");abort();}VkPhysicalDevice* devices = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * deviceCount);vkEnumeratePhysicalDevices(instance, &deviceCount, devices);// 选择可用的物理设备physicalDevice = devices[0];//没啥特殊要求,直接选择第一个设备即可free(devices);// 输出选择的物理设备信息VkPhysicalDeviceProperties deviceProperties;vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);printf("Current Physical Info:ID: %I32u\nName: %s\nVulkan Version: %I32u\n",deviceProperties.deviceID,deviceProperties.deviceName,deviceProperties.apiVersion);// 获取设备队列族的数量uint32_t queueFamilyCount;vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);VkQueueFamilyProperties* queueFamilyProperties = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * queueFamilyCount);vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);// 遍历队列族for (uint32_t i = 0; i < queueFamilyCount; i++){// 支持图形工作if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT){queueFamilyIndex = i;break;}}free(queueFamilyProperties);}/* 创建逻辑设备 */static void CreateLogicalDevice(){// 队列优先级float queuePriority = 1.0f;VkDeviceQueueCreateInfo queueCreateInfo = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,.queueFamilyIndex = queueFamilyIndex,.queueCount = 1,// 只需要创建一个队列.pQueuePriorities = &queuePriority};const char* deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME };VkDeviceCreateInfo createInfo = {.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,.pQueueCreateInfos = &queueCreateInfo,.queueCreateInfoCount = 1,.ppEnabledExtensionNames = deviceExtensionNames,.enabledLayerCount = 0 // 先不开启验证层};VkResult result = vkCreateDevice(physicalDevice, &createInfo, NULL, &logicalDevice);}void InitWindow(int width, int height, const char* title){glfwInit();glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);window = glfwCreateWindow(width, height, title, NULL, NULL);InitInstance(title);PickPhysicalDevice();}int WindowShouldClose(){int flag = glfwWindowShouldClose(window);if (!flag){glfwPollEvents();}return flag;}void CloseWindow(){DestroyInstance();glfwTerminate();}
第三天的地址:/z736248591/article/details/117266221
vulkan的QueueFamilyProperties - 月色疯狂 - 博客园/mooniscrazy/p/11711634.html ↩︎
Vulkan初始化——虚拟逻辑设备 - 知乎 /p/24877337 ↩︎
Vulkan填坑学习Day05—逻辑设备与队列_沉默的舞台剧的博客-CSDN博客 /qq_35312463/article/details/103862429 ↩︎
Vulkan 交换链详解_sy_liao的专栏-CSDN博客 /u010281924/article/details/105368560 ↩︎