Vulkan 围炉夜话1

简介: Vulkan 围炉夜话

Vulkan

Vulkan 官网

Vulkan 参考手册 1.3

参考书

Vulkan学习指南 源码

教程

Vulkan::0.0::开始于VulKanSDK(Getting Started with the Vulkan SDK)

Vulkan Programming Guide::Chapter1::Overview of VulKan(纵观VulKan)

学习摘要

vkGetPhysicalDeviceFeatures —— 负责查询物理设备的特性

VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures(
    VkPhysicalDevice                            physicalDevice,
    VkPhysicalDeviceFeatures*                   pFeatures);
  1. VkPhysicalDeviceFeatures
typedef struct VkPhysicalDeviceFeatures {
    VkBool32    robustBufferAccess;
    VkBool32    fullDrawIndexUint32;
    VkBool32    imageCubeArray;
    VkBool32    independentBlend;
    VkBool32    geometryShader;
    VkBool32    tessellationShader;
    VkBool32    sampleRateShading;
    VkBool32    dualSrcBlend;
    VkBool32    logicOp;
    VkBool32    multiDrawIndirect;
    VkBool32    drawIndirectFirstInstance;
    VkBool32    depthClamp;
    VkBool32    depthBiasClamp;
    VkBool32    fillModeNonSolid;
    VkBool32    depthBounds;
    VkBool32    wideLines;
    VkBool32    largePoints;
    VkBool32    alphaToOne;
    VkBool32    multiViewport;
    VkBool32    samplerAnisotropy;
    VkBool32    textureCompressionETC2;
    VkBool32    textureCompressionASTC_LDR;
    VkBool32    textureCompressionBC;
    VkBool32    occlusionQueryPrecise;
    VkBool32    pipelineStatisticsQuery;
    VkBool32    vertexPipelineStoresAndAtomics;
    VkBool32    fragmentStoresAndAtomics;
    VkBool32    shaderTessellationAndGeometryPointSize;
    VkBool32    shaderImageGatherExtended;
    VkBool32    shaderStorageImageExtendedFormats;
    VkBool32    shaderStorageImageMultisample;
    VkBool32    shaderStorageImageReadWithoutFormat;
    VkBool32    shaderStorageImageWriteWithoutFormat;
    VkBool32    shaderUniformBufferArrayDynamicIndexing;
    VkBool32    shaderSampledImageArrayDynamicIndexing;
    VkBool32    shaderStorageBufferArrayDynamicIndexing;
    VkBool32    shaderStorageImageArrayDynamicIndexing;
    VkBool32    shaderClipDistance;
    VkBool32    shaderCullDistance;
    VkBool32    shaderFloat64;
    VkBool32    shaderInt64;
    VkBool32    shaderInt16;
    VkBool32    shaderResourceResidency;
    VkBool32    shaderResourceMinLod;
    VkBool32    sparseBinding;
    VkBool32    sparseResidencyBuffer;
    VkBool32    sparseResidencyImage2D;
    VkBool32    sparseResidencyImage3D;
    VkBool32    sparseResidency2Samples;
    VkBool32    sparseResidency4Samples;
    VkBool32    sparseResidency8Samples;
    VkBool32    sparseResidency16Samples;
    VkBool32    sparseResidencyAliased;
    VkBool32    variableMultisampleRate;
    VkBool32    inheritedQueries;
} VkPhysicalDeviceFeatures;

vkCreateDevice —— 创建逻辑设备

VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(
    VkPhysicalDevice                            physicalDevice,
    const VkDeviceCreateInfo*                   pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDevice*                                   pDevice);


示例:

  VkResult result;
  float queuePriorities[1]      = { 0.0 };
  VkDeviceQueueCreateInfo queueInfo = {};
  queueInfo.queueFamilyIndex      = graphicsQueueIndex;  
  queueInfo.sType           = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  queueInfo.pNext           = NULL;
  queueInfo.queueCount        = 1;
  queueInfo.pQueuePriorities      = queuePriorities;


  vkGetPhysicalDeviceFeatures(*gpu, &deviceFeatures);

  VkPhysicalDeviceFeatures setEnabledFeatures = {VK_FALSE};
  setEnabledFeatures.samplerAnisotropy = deviceFeatures.samplerAnisotropy;

  VkDeviceCreateInfo deviceInfo   = {};
  deviceInfo.sType          = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  deviceInfo.pNext          = NULL;
  deviceInfo.queueCreateInfoCount   = 1;
  deviceInfo.pQueueCreateInfos    = &queueInfo;
  deviceInfo.enabledLayerCount    = 0;
  deviceInfo.ppEnabledLayerNames    = NULL;                     // Device layers are deprecated
  deviceInfo.enabledExtensionCount  = (uint32_t)extensions.size();
  deviceInfo.ppEnabledExtensionNames  = extensions.size() ? extensions.data() : NULL;
  deviceInfo.pEnabledFeatures     = &setEnabledFeatures;

  result = vkCreateDevice(*gpu, &deviceInfo, NULL, &device);
  1. VkDeviceCreateInfo
typedef struct VkDeviceCreateInfo {
    VkStructureType                    sType;
    const void*                        pNext;
    VkDeviceCreateFlags                flags;
    uint32_t                           queueCreateInfoCount;
    const VkDeviceQueueCreateInfo*     pQueueCreateInfos;
    uint32_t                           enabledLayerCount;
    const char* const*                 ppEnabledLayerNames;
    uint32_t                           enabledExtensionCount;
    const char* const*                 ppEnabledExtensionNames;
    const VkPhysicalDeviceFeatures*    pEnabledFeatures;
} VkDeviceCreateInfo;
  • VkDeviceCreateFlags
typedef enum VkQueueFlagBits {
    VK_QUEUE_GRAPHICS_BIT = 0x00000001,
    VK_QUEUE_COMPUTE_BIT = 0x00000002,
    VK_QUEUE_TRANSFER_BIT = 0x00000004,
    VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
    VK_QUEUE_PROTECTED_BIT = 0x00000010,
#ifdef VK_ENABLE_BETA_EXTENSIONS
    VK_QUEUE_VIDEO_DECODE_BIT_KHR = 0x00000020,
#endif
#ifdef VK_ENABLE_BETA_EXTENSIONS
    VK_QUEUE_VIDEO_ENCODE_BIT_KHR = 0x00000040,
#endif
    VK_QUEUE_OPTICAL_FLOW_BIT_NV = 0x00000100,
    VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkQueueFlagBits;
typedef VkFlags VkQueueFlags;
typedef VkFlags VkDeviceCreateFlags;
  • VkDeviceQueueCreateInfo
typedef struct VkDeviceQueueCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkDeviceQueueCreateFlags    flags;
    uint32_t                    queueFamilyIndex;
    uint32_t                    queueCount;
    const float*                pQueuePriorities;
} VkDeviceQueueCreateInfo;

指令缓存

指令缓存中的指令类型

    指令缓存中记录了一条或者更多条指令。这些指令主要可以划分为以下三种类型:

行为(action):这一类指令会执行诸如绘制、分发、清屏、复制、查询/时间戳,以及子通道的开始/结束等操作。

状态管理(state management):这一类指令包括描述符集合,绑定流水线、缓存,用于设置动态状态、推送常量,以及设置渲染通道/子通道的状态。

同步(synchronization):这一类指令用来执行各种同步:流水线屏障、设置事件、等待事件,以及渲染通道/子通道的依赖。

   Vulkan 提供了硬件队列的一个逻辑视图,每个逻辑视图都会紧密地关联到一个硬件队列。一个 Vulkan 硬件队列可以被多个不同的逻辑队列分别对应,而每个逻辑队列是通过不同的队列参数创建而成的。例如,要显示一幅渲染后的交换链图像,我们可能需要使用指令缓存将它提交给一个图形队列,而后者也是可以用作渲染展示的。


   执行顺序


   指令缓存可以提交到一个队列或者多个队列中:


单队列提交(single queue submission):多个指令缓存提交到一个队列的过程是可以叠加执行的。在单队列提交过程中,指令缓存必须按照具体操作的顺序排列,按照指令顺序和 API 顺序执行。本书中只涉及函数 vkQueueSubmit 的指令提交,并不涉及稀疏内存绑定的指令缓存的问题(通过vkQueueBindSparse)。

多队列提交(multiple queue submission):提交给多个队列的指令缓存可能会按照任何一种顺序执行,除非我们已经通过信号量、屏障等同步机制,显式地定义了排序的约束条件。

vkCreateCommandPool —— 创建指令池

VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool(
    VkDevice                                    device,
    const VkCommandPoolCreateInfo*              pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkCommandPool*                              pCommandPool);
  1. VkCommandPoolCreateInfo
typedef struct VkCommandPoolCreateInfo {
    VkStructureType             sType;
    // 可以设置为扩展结构体的指针或者NULL
    const void*                 pNext;
    // 枚举量,表示指令池和分配指令缓存的方式
    VkCommandPoolCreateFlags    flags;
    // 队列族索引号
    uint32_t                    queueFamilyIndex;
} VkCommandPoolCreateInfo;
  1. 示例
void VulkanRenderer::createCommandPool()
{
  VulkanDevice* deviceObj   = application->deviceObj;
  /* Depends on intializeSwapChainExtension() */
  VkResult  res;

  VkCommandPoolCreateInfo cmdPoolInfo = {};
  cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  cmdPoolInfo.pNext = NULL;
  cmdPoolInfo.queueFamilyIndex = deviceObj->graphicsQueueWithPresentIndex;
  cmdPoolInfo.flags = 0;

  res = vkCreateCommandPool(deviceObj->device, &cmdPoolInfo, NULL, &cmdPool);
  assert(res == VK_SUCCESS);
}

vkAllocateCommandBuffers —— 指令缓存的分配

VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers(
    VkDevice                                    device,
    const VkCommandBufferAllocateInfo*          pAllocateInfo,
    VkCommandBuffer*                            pCommandBuffers);


  1. VkCommandBufferAllocateInfo
typedef struct VkCommandBufferAllocateInfo {
    VkStructureType         sType;
    const void*             pNext;
    // 指令池句柄
    VkCommandPool           commandPool;
    // 位枚举量,表示指令缓存是主级别还是次级别
    VkCommandBufferLevel    level;
    uint32_t                commandBufferCount;
} VkCommandBufferAllocateInfo;
  • VkCommandBufferLevel
typedef enum VkCommandBufferLevel {
    VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0,
    VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1,
    VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF
} VkCommandBufferLevel;
  1. 示例
  VkCommandBufferAllocateInfo cmdInfo = {};
  cmdInfo.sType   = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  cmdInfo.pNext   = NULL;
  cmdInfo.commandPool = cmdPool;
  cmdInfo.level   = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  cmdInfo.commandBufferCount = (uint32_t) sizeof(cmdBuf) / sizeof(VkCommandBuffer);;

  result = vkAllocateCommandBuffers(*device, &cmdInfo, cmdBuf);

vkResetCommandBuffer —— 重新设置指令缓存

VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer(
    VkCommandBuffer                             commandBuffer,
    VkCommandBufferResetFlags                   flags);

vkFreeCommandBuffer —— 释放指令缓存

VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers(
    VkDevice                                    device,
    VkCommandPool                               commandPool,
    uint32_t                                    commandBufferCount,
    const VkCommandBuffer*                      pCommandBuffers);

vkBeginCommandBuffer —— 设置指令记录的起始位置

VKAPI_ATTR VkResult VKAPI_CALL vkBeginCommandBuffer(
    VkCommandBuffer                             commandBuffer,
    const VkCommandBufferBeginInfo*             pBeginInfo);

    这个函数有两个参数。第一个参数是指令缓存的句柄,我们将记录指令到这个缓存当中。第二个参数是一个 VkCommandBufferBeginInfo 结构体,它定义了一些额外的参数,可以设置指令缓存记录过程开始时的不同状态。

  1. VkCommandBufferBeginInfo
typedef struct VkCommandBufferBeginInfo {
    VkStructureType                          sType;
    const void*                              pNext;
    VkCommandBufferUsageFlags                flags;
    const VkCommandBufferInheritanceInfo*    pInheritanceInfo;
} VkCommandBufferBeginInfo;
  1. 示例
  VkCommandBufferInheritanceInfo cmdBufInheritInfo = {};
  cmdBufInheritInfo.sType         = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
  cmdBufInheritInfo.pNext         = NULL;
  cmdBufInheritInfo.renderPass      = VK_NULL_HANDLE;
  cmdBufInheritInfo.subpass       = 0;
  cmdBufInheritInfo.framebuffer     = VK_NULL_HANDLE;
  cmdBufInheritInfo.occlusionQueryEnable  = VK_FALSE;
  cmdBufInheritInfo.queryFlags      = 0;
  cmdBufInheritInfo.pipelineStatistics  = 0;
  
  VkCommandBufferBeginInfo cmdBufInfo = {};
  cmdBufInfo.sType        = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  cmdBufInfo.pNext        = NULL;
  cmdBufInfo.flags        = 0;
  cmdBufInfo.pInheritanceInfo   = &cmdBufInheritInfo;

  result = vkBeginCommandBuffer(cmdBuf, &cmdBufInfo);

vkEndCommandBuffer —— 设置指令记录的结束位置

VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer(
    VkCommandBuffer                             commandBuffer);
  1. 示例
 void CommandBufferMgr::endCommandBuffer(VkCommandBuffer commandBuffer)
{
  VkResult  result;
  result = vkEndCommandBuffer(commandBuffer);
  assert(result == VK_SUCCESS);
}

vkQueueSubmit —— 队列提交

    将指令记录到指令缓存(VkCommandBuffer)之后,就可以将它提交给一个队列了。函数 vkQueueSubmit() 可以帮助我们将工作内容提交给对应的队列。

VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(
    VkQueue                                     queue,
    uint32_t                                    submitCount,
    const VkSubmitInfo*                         pSubmits,
    VkFence                                     fence);
  1. VkSubmitInfo
typedef struct VkSubmitInfo {
    VkStructureType                sType;
    const void*                    pNext;
    uint32_t                       waitSemaphoreCount;
    const VkSemaphore*             pWaitSemaphores;
    const VkPipelineStageFlags*    pWaitDstStageMask;
    uint32_t                       commandBufferCount;
    const VkCommandBuffer*         pCommandBuffers;
    uint32_t                       signalSemaphoreCount;
    const VkSemaphore*             pSignalSemaphores;
} VkSubmitInfo;
  1. 示例
void CommandBufferMgr::submitCommandBuffer(const VkQueue& queue, const VkCommandBuffer* commandBuffer, const VkFence& fence)
{
  VkResult result;  
  // Otherwise, create the submit information with specified buffer commands
  VkSubmitInfo submitInfo = {};
  submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  submitInfo.pNext        = NULL;
  submitInfo.waitSemaphoreCount = 0;
  submitInfo.pWaitSemaphores    = NULL;
  submitInfo.pWaitDstStageMask  = NULL;
  submitInfo.commandBufferCount = (uint32_t)sizeof(commandBuffer)/sizeof(VkCommandBuffer);
  submitInfo.pCommandBuffers    = commandBuffer;
  submitInfo.signalSemaphoreCount = 0;
  submitInfo.pSignalSemaphores  = NULL;

  result = vkQueueSubmit(queue, 1, &submitInfo, fence);
  assert(!result);

  result = vkQueueWaitIdle(queue);
  assert(!result);
}

vkQueueWaitIdle —— 队列等待

    当应用程序将指令缓存提交到队列中以后,它会等待队列完成提交工作并且准备好接收下一个批次的内容。等待队列完成的API函数为 vkQueueWaitIdle 。

VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle(
    VkQueue                                     queue);

VkImage 、VkBuffer

VkImage 相关

    完整的图像资源创建工作流如下图:

vkCreateImage —— 创建图像资源
VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage(
    VkDevice                                    device,
    const VkImageCreateInfo*                    pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkImage*                                    pImage);
  1. VkImageCreateInfo
typedef struct VkImageCreateInfo {
    VkStructureType          sType;
    const void*              pNext;
    VkImageCreateFlags       flags;
    VkImageType              imageType;   // 设置图像的 1D/2D/3D 维度
    VkFormat                 format;    // 图像格式
    VkExtent3D               extent;    // 图像在 mipmap 基础级别的每个维度的元素数量
    uint32_t                 mipLevels;   // 图像 mipmap 采样的级数
    uint32_t                 arrayLayers; // 图像数组的层数
    VkSampleCountFlagBits    samples;   // 图像中的子元素采样数
    VkImageTiling            tiling;    // 图像在内存中进行重复贴图的方式
    VkImageUsageFlags        usage;     // 图像内在用途
    VkSharingMode            sharingMode; // 图像在多个队列族之间的共享模式
    uint32_t                 queueFamilyIndexCount; // 数组 pQueueFamilyIndices 的元素个数
    const uint32_t*          pQueueFamilyIndices; // 准备访问图像的队列族的数组
    VkImageLayout            initialLayout; // 图像所有子资源的布局
} VkImageCreateInfo;
  • VkImageCreateFlagBits
typedef enum VkImageCreateFlagBits {
    VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001,
    VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002,
    VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004,
    VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008,
    VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010,
  ...
    VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkImageCreateFlagBits;
typedef VkFlags VkImageCreateFlags;
  • VkImageType
typedef enum VkImageType {
    VK_IMAGE_TYPE_1D = 0,
    VK_IMAGE_TYPE_2D = 1,
    VK_IMAGE_TYPE_3D = 2,
    VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF
} VkImageType;
  • VkFormat
typedef enum VkFormat {
    VK_FORMAT_UNDEFINED = 0,
    VK_FORMAT_R4G4_UNORM_PACK8 = 1,
    VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2,
    VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3,
    VK_FORMAT_R5G6B5_UNORM_PACK16 = 4,
    VK_FORMAT_B5G6R5_UNORM_PACK16 = 5,
  ...
    VK_FORMAT_MAX_ENUM = 0x7FFFFFFF
} VkFormat;
  • VkExtent3D
typedef struct VkExtent3D {
    uint32_t    width;
    uint32_t    height;
    uint32_t    depth;
} VkExtent3D;
  • VkSampleCountFlagBits
typedef enum VkSampleCountFlagBits {
    VK_SAMPLE_COUNT_1_BIT = 0x00000001,
    VK_SAMPLE_COUNT_2_BIT = 0x00000002,
    VK_SAMPLE_COUNT_4_BIT = 0x00000004,
    VK_SAMPLE_COUNT_8_BIT = 0x00000008,
    VK_SAMPLE_COUNT_16_BIT = 0x00000010,
    VK_SAMPLE_COUNT_32_BIT = 0x00000020,
    VK_SAMPLE_COUNT_64_BIT = 0x00000040,
    VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkSampleCountFlagBits;
typedef VkFlags VkSampleCountFlags;
  • VkImageTiling
typedef enum VkImageTiling {
    VK_IMAGE_TILING_OPTIMAL = 0,
    VK_IMAGE_TILING_LINEAR = 1,
    VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT = 1000158000,
    VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF
} VkImageTiling;
  • VkImageUsageFlagBits
typedef enum VkImageUsageFlagBits {
    VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001,
    VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002,
    VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004,
    VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008,
    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010,
    VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020,
    VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040,
  ...
    VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkImageUsageFlagBits;
typedef VkFlags VkImageUsageFlags;
  • VkSharingMode
typedef enum VkSharingMode {
    VK_SHARING_MODE_EXCLUSIVE = 0,
    VK_SHARING_MODE_CONCURRENT = 1,
    VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF
} VkSharingMode;
  • VkImageLayout
typedef enum VkImageLayout {
    VK_IMAGE_LAYOUT_UNDEFINED = 0,
    VK_IMAGE_LAYOUT_GENERAL = 1,
    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2,
    VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3,
  ...
    VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF
} VkImageLayout;
示例
  VkImageCreateInfo imageCreateInfo = {};
  imageCreateInfo.sType     = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  imageCreateInfo.pNext     = NULL;
  imageCreateInfo.imageType   = VK_IMAGE_TYPE_2D;
  imageCreateInfo.format      = format;
  imageCreateInfo.mipLevels   = texture->mipMapLevels;
  imageCreateInfo.arrayLayers   = 1;
  imageCreateInfo.samples     = VK_SAMPLE_COUNT_1_BIT;
  imageCreateInfo.tiling      = VK_IMAGE_TILING_OPTIMAL;
  imageCreateInfo.sharingMode   = VK_SHARING_MODE_EXCLUSIVE;
  imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  imageCreateInfo.extent      = { texture->textureWidth, texture->textureHeight, 1 };
  imageCreateInfo.usage     = imageUsageFlags;

  // Set image object with VK_IMAGE_USAGE_TRANSFER_DST_BIT if
  // not set already. This allows to copy the source VkBuffer 
  // object (with VK_IMAGE_USAGE_TRANSFER_DST_BIT) contents
  // into this image object memory(destination).
  if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)){
    imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
  }

  error = vkCreateImage(deviceObj->device, &imageCreateInfo, nullptr, &texture->image);

Vulkan 围炉夜话2:https://developer.aliyun.com/article/1598109

目录
相关文章
|
3月前
|
存储 缓存 图形学
Vulkan 围炉夜话3
Vulkan 围炉夜话
42 9
Vulkan 围炉夜话3
|
3月前
|
缓存 并行计算 算法
Vulkan 围炉夜话2
Vulkan 围炉夜话
54 13
|
3月前
|
存储 缓存 API
Vulkan 围炉夜话4
Vulkan 围炉夜话
40 5
|
3月前
|
存储 缓存 算法
Vulkan 围炉夜话5
Vulkan 围炉夜话
37 4
|
3月前
|
Unix API 图形学
OpenGL 围炉夜话
OpenGL 围炉夜话
37 10
|
3月前
|
Linux
BuildRoot 围炉夜话
BuildRoot 围炉夜话
70 9
|
3月前
|
Java 虚拟化 数据安全/隐私保护
MacOs 围炉夜话
MacOs 围炉夜话
80 10
|
3月前
|
开发工具 Android开发 iOS开发
多端开发围炉夜话
多端开发围炉夜话
31 12
|
3月前
|
物联网 Linux 编译器
IOT 围炉札记
IOT 围炉札记
19 0
IOT 围炉札记
|
6月前
|
传感器 人工智能 机器人
Tecno推出机器狗Dynamic 1
【2月更文挑战第7天】Tecno推出机器狗Dynamic 1
80 3
Tecno推出机器狗Dynamic 1