游戏引擎开发日志 (第四天 2021年6月8日)

Posted 雪靡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏引擎开发日志 (第四天 2021年6月8日)相关的知识,希望对你有一定的参考价值。

第四天 2021年6月8日

上一天(第三天)的地址: https://blog.csdn.net/z736248591/article/details/117266221


最近有点忙,快到期末了,很多课程都要结课,作业巨多。但是时间是挤出来的。

继续之前的交换链函数的完成。

先获取表面的能力

// 获取表面能力
VkSurfaceCapabilitiesKHR capabilities;
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice,surface,&capabilities);
assert(result == VK_SUCCESS);

由于c未包含min和max函数,这里使用宏定义一个。位于common.h文件内。

#ifndef max
#define max(x,y) (x>y?x:y)
#endif

#ifndef min
#define min(x,y) (x<y?x:y)
#endif

确定表面宽度和高度。

// 确定表面的尺寸
VkExtent2D swapchainExtent;
//如果尺寸未定义
if (capabilities.currentExtent.width == 0xFFFFFFFF)
{
    swapchainExtent.width =
        max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, width));
    swapchainExtent.height =
        max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, height));
}
else
{
    //如果有确定的尺寸
    swapchainExtent = capabilities.currentExtent;
}
screenWidth = swapchainExtent.width;
screenHeight = swapchainExtent.height;

// 交换链图像的数量
uint32_t imageCount = capabilities.minImageCount +1;
if((capabilities.maxImageCount>0)&&(imageCount>capabilities.maxImageCount))
{
    imageCount = capabilities.maxImageCount;
}

准备创建交换链,填充创建信息。

//构建交换链创建信息结构体实例
VkSwapchainCreateInfoKHR createInfo = {
    .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
    .surface = surface,
    .minImageCount = imageCount,
    .imageFormat = VK_FORMAT_B8G8R8A8_UNORM,
    .imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
    .imageExtent = swapchainExtent,
    .imageArrayLayers = 1,        // 2D游戏只需要1层
    .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
    .presentMode = presentMode,     // 不与其他窗口混合
    .clipped = VK_TRUE,
    .oldSwapchain = VK_NULL_HANDLE
};

这里在添加CreateSwap函数内,添加检测家族队列是否支持Present(显示工作)工作的代码。

// 遍历设备对应的队列家族列表
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->queueCount >0 &&queueFamilyProperties->queueFlags&VK_QUEUE_GRAPHICS_BIT)
    {
        // 记录支持Graphis(图形)工作的队列家族索引
        queueGraphicsFamilyIndex = i;
    }
    VkBool32 presentSupport = false;
    vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice,i,surface,&presentSupport);
    // 如果当前遍历到的队列家族支持Present(显示工作)工作
    if(presentSupport)
    {
        queuePresentFamilyIndex = i;
    }
    // 如果找到支持图形工作的队列家族索引和支持显示工作工作的队列家族索引
    if(queueGraphicsFamilyIndex!=UINT32_MAX&&queuePresentFamilyIndex!=UINT32_MAX)
    {
        break;
    }
}
free(queueFamilyProperties);

if(queueGraphicsFamilyIndex==UINT32_MAX||queuePresentFamilyIndex==UINT32_MAX)
{
    printf("Cannot find a or two queue family with Graphics or Present support!");
    abort();
}

这里我有个疑惑。在之前生成逻辑设备的时候,用到了queueGraphicsFamilyIndex这个变量,那么现在变量的数值更改了,逻辑设备是否需要重新生成?

接下来继续创建交换链

if(queueGraphicsFamilyIndex != queuePresentFamilyIndex)
{
    // 如果支持图形和显示工作的队列家族不同
    createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
    createInfo.queueFamilyIndexCount = 2;
    uint32_t queueFamilyIndices[2]={queueGraphicsFamilyIndex,queuePresentFamilyIndex};
    createInfo.pQueueFamilyIndices = queueFamilyIndices;
}
else
{
    createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
    createInfo.queueFamilyIndexCount = 0;
    createInfo.pQueueFamilyIndices = NULL;
}

如果graphics队列簇与presentation队列簇不同,会出现如下情形。我们将从graphics队列中绘制交换链的图像,然后在另一个presentation队列中提交他们。多队列处理图像有两种方法:

1、VK_SHARING_MODE_EXCLUSIVE: 同一时间图像只能被一个队列簇占用,如果其他队列簇需要其所有权需要明确指定。这种方式提供了最好的性能。
2、VK_SHARING_MODE_CONCURRENT: 图像可以被多个队列簇访问,不需要明确所有权从属关系。
如果队列簇不同,将会使用concurrent模式,避免处理图像所有权从属关系的内容。Concurrent模式需要预先指定队列簇所有权从属关系,通过queueFamilyIndexCountpQueueFamilyIndices参数进行共享。如果graphics队列簇和presentation队列簇相同,我们需要使用exclusive模式,因为concurrent模式需要至少两个不同的队列簇1

判断是否支持旋转:

// 判断是否支持旋转
if(capabilities.supportedTransforms &VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
{
    createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
}
else
{
    createInfo.preTransform = capabilities.currentTransform;
}

现在添加全局变量存储VkSwapchainKHR对象:

VkSwapchainKHR swapchain;
// 创建交换链
result = vkCreateSwapchainKHR(logicalDevice,&createInfo,NULL,&swapchain);
assert(result == VK_SUCCESS);

接下来,获取交换链中的图像数量。

在全局添加

// 交换链图像
static VkImage *swapchainImages;
// 获取交换链图像
vkGetSwapchainImagesKHR(logicalDevice,swapchain,&imageCount,NULL);
swapchainImages = (VkImage*)malloc(sizeof(VkImage)*imageCount);
result = vkGetSwapchainImagesKHR(logicalDevice,swapchain,&imageCount,swapchainImages);
assert(result);

创建图像视图:

在全局添加:

// 图像视图
static VkImageView *swapchainImageViews;
// 交换链图像视图列表
swapchainImageViews = (VkImageView*)malloc(sizeof(VkImageView) * imageCount);
for (uint32_t i = 0; i < imageCount; i++)
{
    VkImageViewCreateInfo imageViewCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        .image = swapchainImages[i],
        .viewType = VK_IMAGE_VIEW_TYPE_2D,
        .format = VK_FORMAT_B8G8R8A8_UNORM,
        // 颜色通道的最终的映射逻辑
        .components = {
            .r = VK_COMPONENT_SWIZZLE_IDENTITY,
            .g = VK_COMPONENT_SWIZZLE_IDENTITY,
            .b=VK_COMPONENT_SWIZZLE_IDENTITY,
            .a = VK_COMPONENT_SWIZZLE_IDENTITY
        },
        // 图像视图使用方面
        .subresourceRange = {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .baseMipLevel = 0,
            .levelCount = 1,
            .baseArrayLayer = 0,
            .layerCount  =1
        },
    };
    // 创建图像视图
    result = vkCreateImageView(logicalDevice, &imageViewCreateInfo, NULL, &swapchainImageViews[i]);
    assert(result == VK_SUCCESS);
}

最后记得销毁:

/* 销毁交换链 */
void DestroyVulkanSwapchain()
{
    for(uint32_t i=0;i<swapchainImageCount;i++)
    {
        vkDestroyImageView(logicalDevice,swapchainImageViews[i],NULL);
    }
    vkDestroySwapchainKHR(logicalDevice,swapchain,NULL);

    free(swapchainImages);
    free(swapchainImageViews);
}

由于我们这里出错了,也找不到错误,之后我们给原来的代码添加添加验证层。

VK_LAYER_LUNARG_standard_validation已经被弃用2

此层不再使用,请使用VK_LAYER_KHRONOS_validation去替代VK_LAYER_LUNARG_standard_validation层。

剩下的,下一天继续。


  1. Vulkan填坑学习Day06—交换链_沉默的舞台剧的博客-CSDN博客 https://blog.csdn.net/qq_35312463/article/details/103880221?spm=1001.2014.3001.5501 ↩︎

  2. https://vulkan.lunarg.com/doc/view/1.1.114.0/windows/validation_layers.html ↩︎

以上是关于游戏引擎开发日志 (第四天 2021年6月8日)的主要内容,如果未能解决你的问题,请参考以下文章

游戏引擎开发日志 (第一天 2021年5月23日)

游戏引擎开发日志(第二天)

2021 夏季 Steam 游戏节将于 6月16 日举行,为期 6 天

C++第十四天笔记2016年03月10日(周四) A.M

游戏引擎开发日志(第三天)

游戏引擎开发日志(第三天)