vulkan学习笔记十七

创建VkImage和VkImageView

在SceneWidget.h中添加接口bool CreateTextureImageView(),VkImage只是保存了图片的资源和数据,要想展示出来,就必需得有VkImageView。在这个函数里创建,程序结束时还要清理,所以需要声明两个成员变量,保存生成的VkImage和VkImageView的对象。

添加成员:VkImage m_image = VK_NULL_HANDLE; VkImageView m_imageView = VK_NULL_HANDLE,每个VkImage对象的创建还对应一个 VkDeviceMemory。所以还有再添加一个成员 VkDeviceMemory m_ImageMemory = VK_NULL_HANDLE。

按vulkan编程指南教程中的思路,需要先加载一个张图片。在电脑上找到一张图

文件名为test.png。先放到工程目录下。

然后可以CreateTextureImageView函数里加载这个图,利用Qt的QImage类,进行加载。

QImage image;

image.load(“test.png”);

来进行加载。

然后通过

QImage::Format format = image.format();

unsigned char* pixelData = image.bits();

来获取像素格式和rgba的数据。

然后申请临时的VkBuffer imageBuffer = VK_NULL_HANDLE;

和临时的VkDeviceMemory imageMemory = VK_NULL_HANDLE;

调用CreateBuffer,参数为:

第一个参数:像素通道数*图片的宽*图片的高。这里是4*image.width()*image.height()数据类型为VkDeviceSize类型。

第二个参数:VK_BUFFER_USAGE_TRANSFER_SRC_BIT,是映射到cpu端的内存,做为源数据缓存使用。

第三个参数:VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT在cpu端的内存,对显卡可见,且访问时不需要特殊操作。

第四个参数:为imageBuffer。

第五个参数:为imageMemory

通过vkMapMamory把数据映射到void* data上。再将cpu的数据memcpy到GPU显存中。解除映射。通过图片的数据信息创建m_image和m_imageMemory。即调用:

CreateImage(),其参数:

第一个参数:image.width()图片的宽

第二个参数:image.height()图片的高。

第三个参数:图片可类似与mipmap,存有多个图层,这个参数表示当前所在的图层,这个图没有分层级,这里设置为1.

第四个参数:采样数,VkSampleCountFlagBits的值,这里取VK_SAMPLE_COUNT_1_BIT,尽可能小的采样,使图片保真。

第五个参数:图片的像素格式。这里为VK_FORMAT_R8G8B8A8_SRGB

第六个参数:图片的平铺方式。这里为VK_IMAGE_TILING_OPTIMAL

第七个参数:图片的例用,可做为源和目的数据,且可用于采样。

第八个参数:这里是从本地取图片数据。

第九个参数:m_image。取出生成的image对象。

第十个参数:m_imageMemory。

还是和vertexBuffer的处理一样,要通过copyBuffer这个运作将图片的数据拷贝到显卡来直接使用。

但是图片的数据,要使用copy函数,就得将内存的布局变换一下,所以需要再添加一个辅助函数:void TransitionImageLayout()函数,参数

第一个参数:要转换的VkImage对象

第二个参数:VkImage的像素格式。

第三个参数:VkImage原有的内存布局

第四个参数:要转换后的内存布局。

第五个参数:当前VkImage所在的图层的层级。

在CreateTextureImageView调用这个TransitionImageLayout(),传入参数:

第一个参数:m_image

第二个参数:VK_FORMAT_R8G8B8A8_SRGB,是这个图片当前的像素格式。

第三个参数:VK_IMAGE_LAYOUT_UNDEFINED,获得更好的性能表现。

第四个参数:VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,转换成适合输出到目的的内存布局。

第五个参数:1,如果图片中有包含多层级数据,这个值就要根据真实的层级来输入了。

然后就是将imageBuffer中的数据拷贝到m_image中,再添加一个辅助函数:void CopyBufferToImage(),参数为:

第一个参数:要拷贝的imageBuffer

第二个参数:m_image,

第三个参数:图片的宽

第四个参数:图片的高

拷贝完成后,清理imageBuffer和对应的Memory。

vkDestroyBuffer(m_device, imageBuffer, nullptr);

vkFreeMemory(m_device, imageMemory, nullptr);

函数的最后,调用CreateImageView接口,生成m_imageView.

最后就是提交image.还要再添加一个函数SubmmitImage()按vulkan编程指南上讲解的,还要设置Barrier,所需要参数:

第一个参数:m_image

第二个参数:像素格式,VK_FORMAT_R8G8B8A8_SRGB

第三个参数:图片的宽

第四个参数:图片的高

现在需要补齐刚刚添加的这三个函数:

TransitionImageLayout:

内存布局的转换,是通过设置VkImageMemoryBarrier数据结构,并调用vkCmdPipelineBarrier函数进行转换。

先定义一个VkImageMemoryBarrier barrier = {}并赋值:

固定写法

barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;

保持默认

barrier.pNext = nullptr;

要转换的布局

barrier.oldLayout = oldLayout;

转换后的布局

barrier.newLayout = newLayout;

不需要转移队列的所有权。

barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

要转换的image。

barrier.image = image;

设置subresourceRange,与颜色缓存绑定、层级从0开始、层的总数。

barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;

barrier.subresourceRange.baseMipLevel = 0;

barrier.subresourceRange.levelCount = mipLevels;

第一个arrayLayer

barrier.subresourceRange.baseArrayLayer = 0;

arrayLayer的总数

barrier.subresourceRange.layerCount = 1;

在vkCmdPipelineBarrier函数中还有两个参数为VkPipelineStageFlags类型,一个是源数据的,一个是目的数据的。现在定义两个变量VkPipelineStageFlags sourceStage, destStage;

sourceStage, destStage两个的值和barrier.srcAccessMask, barrier.dstAccessMask;的值根据参数中调用时传入的oldLayout和newLayout有关,由于之前输入时,

oldLayout = VK_IMAGE_LAYOUT_UNDEFINED

newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL

所以

barrier.srcAccessMask = 0, barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT

队列最开始阶段时,可接受命令

sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;

支持copy命令

destStage = VK_PIPELINE_STAGE_TRANSFER_BIT;

还差一个commandBuffer,通过之前封装的BeginSingleTimeCommands函数来取得。

VkCommandBuffer commandBuffer = BeginSingleTimeCommands()

参数设置完成,调用vkCmdPipelineBarrier()其参数:

第一个参数:commandBuffer,

第二个参数:sourceStage

第三个参数:destStage

第四个参数:不需要,为0

第五个参数:不需要,为0

第六个参数:为nullptr.

第七个参数:为0

第八个参数:为nullptr.

第九个参数:为1

第十个参数:为&barrier

函数最后,调用EndCommands结束提交。

CopyBufferToImage:

在这个函数,调用vkCmdCopyBufferToImage进行拷贝操作,其参数:

第一个参数commandBuffer,可以像上一个函数一样,通过BeginCommands获取。

第二个参数:使用函数传入的参数buffer

第三个参数:使用函数传入的参数:image

第四个参数:之前都是用的VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,这里保持一致

第五个参数:这里拷贝一张图片,设置为1

第六个参数:VkBufferImageCopy类型的指针,所以这里补充一个变量VkBufferImageCopy region = {}

region属性的赋值:

在数据buffer中和偏移,这里为0

region.bufferOffset = 0;

bufferRowLength和bufferImageHeight设置为0,图片的范围根据imageExtent计算

region.bufferRowLength = 0;

region.bufferImageHeight = 0;

与颜色缓冲区绑定

region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;

层级为0

region.imageSubresource.mipLevel = 0;

region.imageSubresource.baseArrayLayer = 0;

共一层

region.imageSubresource.layerCount = 1;

图片的偏移为000

region.imageOffset = { 0, 0, 0 };

图片的宽高,二维图片深度默认为1

region.imageExtent = {width, height, 1};

与BeginCommands对应,调用EndCommands

SubmmitImage:

先检查设备,是否支持线性采样,可通过vkGetPhysicalDeviceFormatProperties查找采样属性,检查是否支持VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT特性。

其参数:

第一个参数:为物理设备对象,m_physicalDevice

第二个参数:函数的参数,format.

第三个参数:VkFormatProperties类型的传出参数formatProperties。

检查formatProperties.optimalTilingFeatures特性是否包含VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT,一般使用与操作。

同样在这个函数里,需要处理VkImageMemoryBarrier,调用vkCmdPipelineBarrier函数。

vkCmdPipelineBarrier函数的参数:

第一个参数:commandBuffer,同样从BeginCommands获取,

第二个参数:VK_PIPELINE_STAGE_TRANSFER_BIT源数据支持copy

第三个参数:VK_PIPELINE_STAGE_TRANSFER_BIT目的数据支持copy

第四个参数:不需要,设置为0

第五个参数:不需要设置为0

第六个参数:nullptr

第七个参数:0

第八个参数:nullptr.

第九个参数:VkImageMemoryBarrier数组的个数为1

第十个参数:VkImageMemoryBarrier类型的数组指针,需要加一个变量VkImageMemoryBarrier barrier = {};

其属性赋值:

固定写法

barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;

保持默认

barrier.pNext = nullptr;

barrier.srcAccessMask = 0;

barrier.dstAccessMask = 0;

这是操作在目的数据上的,所以是:

barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;

只从shader里读取

barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

还是VK_QUEUE_FAMILY_IGNORED;

barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

barrier.image = image;

barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;

barrier.subresourceRange.baseArrayLayer = 0;

barrier.subresourceRange.layerCount = 1;

barrier.subresourceRange.levelCount = 1;

barrier.subresourceRange.baseMipLevel = 0;

在函数最后,调用EndCommands

然后在SceneWidget::Init中添加调用,并在SceneWidget::UnInit中添加清理:

vkFreeMemory(m_device, m_imageMemory, nullptr);

vkDestroyImage(m_device, m_image, nullptr);

vkDestroyImageView(m_device, m_imageView, nullptr);

最后执行代码,看一下效果:

没有错误提示:) 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值