游戏引擎开发日志(第二天)
Posted 雪靡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏引擎开发日志(第二天)相关的知识,希望对你有一定的参考价值。
上一天的地址:https://blog.csdn.net/z736248591/article/details/117201596
————————————哥是可爱的分割线————————————————
第二天 2021年5月24日
回顾:上一天创建了项目,决定了目标和工具。今天继续。
这里使用GLFW作为渲染库。
GLFW介绍:
GLFW is 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.1
DESCRIPTION "A 2D multi-platform game engine"
HOMEPAGE_URL "https://github.com/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)
# 找到OpenGL
find_package(OpenGL REQUIRED)
# 将源代码添加到此项目的可执行文件。
add_executable (TheSeedGameEngine
src/main.c)
target_link_libraries(TheSeedGameEngine
glfw
OpenGL::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,就只有一个队列。可以图形、计算、传输、稀疏矩阵。
独立显卡是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: 7308
Name: GeForce GTX 1050 Ti
Vulkan Version: 4202651
Vulkan 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();
}
第三天的地址:https://blog.csdn.net/z736248591/article/details/117266221
vulkan的QueueFamilyProperties - 月色疯狂 - 博客园https://www.cnblogs.com/mooniscrazy/p/11711634.html ↩︎
Vulkan初始化——虚拟逻辑设备 - 知乎 https://zhuanlan.zhihu.com/p/24877337 ↩︎
Vulkan填坑学习Day05—逻辑设备与队列_沉默的舞台剧的博客-CSDN博客 https://blog.csdn.net/qq_35312463/article/details/103862429 ↩︎
Vulkan 交换链详解_sy_liao的专栏-CSDN博客 https://blog.csdn.net/u010281924/article/details/105368560 ↩︎
以上是关于游戏引擎开发日志(第二天)的主要内容,如果未能解决你的问题,请参考以下文章