Vulkan 围炉夜话5

简介: Vulkan 围炉夜话

vkCmdSetEvent,vkCmdResetEvent —— 设备端发送信号或者重置信号给事件

   设备端的事件可以通过指令缓存更新为“已设置”或者“已重置”这里用到了 vkCmdSetEvent() 和 vkCmdResetEvent() 函数来向事件发送信号或者重置信号。


   这两个函数都有三个参数:第一个参数 commandBuffer 设置了录制指令时用到的指令缓存;第二个参数 event 设置了事件对象的句柄,我们可以向它发送信号或者重置;最后一个参数 stageMask 的类型为 VkPipelineStageFlags,即流水线阶段。表示这个事件需要在什么阶段进行更新。

VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent(
    VkCommandBuffer                             commandBuffer,
    VkEvent                                     event,
    VkPipelineStageFlags                        stageMask);

VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent(
    VkCommandBuffer                             commandBuffer,
    VkEvent                                     event,
    VkPipelineStageFlags                        stageMask);

vkCmdWaitEvents —— 等待事件对象

VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents(
    VkCommandBuffer                             commandBuffer,
    uint32_t                                    eventCount,
    const VkEvent*                              pEvents,
    VkPipelineStageFlags                        srcStageMask,
    VkPipelineStageFlags                        dstStageMask,
    uint32_t                                    memoryBarrierCount,
    const VkMemoryBarrier*                      pMemoryBarriers,
    uint32_t                                    bufferMemoryBarrierCount,
    const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
    uint32_t                                    imageMemoryBarrierCount,
    const VkImageMemoryBarrier*                 pImageMemoryBarriers);

理解描述符的概念

  描述符由描述符集对象构成。这些对象的内部负责记录一组描述符。描述符集可以关联着色器和相关用户资源,例如一致变量缓存、采样图像、存储图像等,进而在着色器中读取和解析资源中的数据,着色器中需要通过描述符集布局来定义对应的布局绑定信息。例如,我们可以通过描述符实现图像纹理、采样器以及缓存等资源与着色器的绑定。描述符是一种不透明的对象,它定义了一种通信协议和着色器进行通信。在系统内部,描述符提供了一种静默的机制,通过位置绑定的方式来关联资源内存和着色器。

描述符集布局

   描述符集布局指的是一组描述符绑定信息,可以是 0 个元素或者更多。它提供了一个在着色器中读取特定位置的资源的接口。每个描述符绑定信息都有自己特定的类型,对应于准备处理的资源类型,其中包含了绑定中的描述符数量,采样描述符的数组,以及与之相关联的着色器阶段。这些元数据都是在 VkDescriptorSetLayoutBinding 中定义的。如下图所示为描述符集的布局内容,其中包含了多种不同的资源布局绑定信息,在描述符布局中定义的每个资源都设置了一个唯一的标识绑定数字。

vkCreateDescriptorSetLayout —— 创建描述符集布局

    描述符集布局的创建可以通过 API 函数 vkCreateDescriptorSetLayout() 来完成。这个函数的输入参数包括一个 VkDescriptorSetLayoutCreateInfo 结构体,用于处理零个到多个描述符集的元数据信息,其中还包含了一个 VkDescriptorSetLayoutBinding 结构体。

VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorSetLayout(
    VkDevice                                    device,
    const VkDescriptorSetLayoutCreateInfo*      pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDescriptorSetLayout*                      pSetLayout);
  1. VkDescriptorSetLayoutCreateInfo
typedef struct VkDescriptorSetLayoutCreateInfo {
    VkStructureType                        sType;
    const void*                            pNext;
    VkDescriptorSetLayoutCreateFlags       flags;
    // 描述符集布局绑定数量
    uint32_t                               bindingCount;
    // 描述符集布局绑定对象
    const VkDescriptorSetLayoutBinding*    pBindings;
} VkDescriptorSetLayoutCreateInfo;
  1. VkDescriptorSetLayoutBinding
typedef struct VkDescriptorSetLayoutBinding {
  // 设置绑定索引值,即当前资源类型,这个索引值必须和对应着色器阶段中标识的绑定数值相同
    uint32_t              binding;
    // 设置绑定所用的描述符类型
    VkDescriptorType      descriptorType;
    // 设置着色器中用到的描述符的数组元素数量
    uint32_t              descriptorCount;
    // 设置当前可以访问描述符的着色器阶段类型
    VkShaderStageFlags    stageFlags;
    // 设置采样器句柄的数组指针
    const VkSampler*      pImmutableSamplers;
} VkDescriptorSetLayoutBinding;

vkDestroyDescriptorSetLayout —— 销毁描述符集布局

VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorSetLayout(
    VkDevice                                    device,
    VkDescriptorSetLayout                       descriptorSetLayout,
    const VkAllocationCallbacks*                pAllocator);


流水线布局

理解流水线布局

   流水线布局允许一个流水线(图形或者计算)对象直接访问描述符集。流水线布局对象包含了一组描述符集布局和推送常数,它代表了底层流水线可以访问的所有资源的集合。


   流水线布局对象的属性信息通过结构体 VkGraphicsPipelineCreateInfo 来提供,然后传递给 API 函数 vkCreateGraphicsPipelines() 来创建流水线。这些属性信息是通过参数 VkGraphicsPipelineCreateInfo::layout 设置的。这个参数是必须设置的。如果应用程序没有用到描述符集,那么这里必须设置一个空的描述符布局并且将对应的流水线布局设置给流水线对象 (VkPipeline)的创建函数。


   流水线布局中可以连续包含零个或者多个描述符集,每个集合中都包含了一个特定的布局对象。布局定义了着色器阶段和着色器资源之间的接口。如下图所示为流水线布局的内容,其中包括了多个描述符布局,每个布局中包含了针对多种不同的资源类型的布局绑定。

vkCreatePipelineLayout —— 创建流水线布局

VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineLayout(
    VkDevice                                    device,
    const VkPipelineLayoutCreateInfo*           pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkPipelineLayout*                           pPipelineLayout);
  1. VkPipelineLayoutCreateInfo
typedef struct VkPipelineLayoutCreateInfo {
    VkStructureType                 sType;
    const void*                     pNext;
    VkPipelineLayoutCreateFlags     flags;
    uint32_t                        setLayoutCount;
    const VkDescriptorSetLayout*    pSetLayouts;
    uint32_t                        pushConstantRangeCount;
    const VkPushConstantRange*      pPushConstantRanges;
} VkPipelineLayoutCreateInfo;

typedef enum VkPipelineLayoutCreateFlagBits {
    VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT = 0x00000002,
    VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkPipelineLayoutCreateFlagBits;
typedef VkFlags VkPipelineLayoutCreateFlags;
typedef VkFlags VkShaderStageFlags;

typedef struct VkPushConstantRange {
  // 定义了推送常数范围所对应的着色器阶段,如 VK_SHADER_STAGE_FRAGMENT_BIT
  // VK_SHADER_STAGE_VERTEX_BIT,VK_SHADER_STAGE_COMPUTE_BIT 
    VkShaderStageFlags    stageFlags;
    // 这个参数设置了推送常数范围的起始偏移值,单位为字节,且必须是 4 的倍数
    uint32_t              offset;
    // 这个参数单位也是字节,且必须是 4 的倍数,它设置了推送常数范围的数据大小
    uint32_t              size;
} VkPushConstantRange;

vkDestroyPipelineLayout —— 销毁流水线布局

VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineLayout(
    VkDevice                                    device,
    VkPipelineLayout                            pipelineLayout,
    const VkAllocationCallbacks*                pAllocator);

描述符池

vkCreateDescriptorPool —— 创建描述符池

    Vulkan 当中的描述符集是不能被直接创建的。它们首先需要从一个特定的缓冲池中被分配得到,这个池叫作描述符池。描述符池负责分配新的描述符对象。换句话说,它相当于一组描述符的集合,新的描述符集就是从这些描述符中分配得到的。

VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorPool(
    VkDevice                                    device,
    const VkDescriptorPoolCreateInfo*           pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDescriptorPool*                           pDescriptorPool);
  1. VkDescriptorPoolCreateInfo
typedef struct VkDescriptorPoolCreateInfo {
    VkStructureType                sType;
    const void*                    pNext;
    VkDescriptorPoolCreateFlags    flags;
    uint32_t                       maxSets;
    uint32_t                       poolSizeCount;
    const VkDescriptorPoolSize*    pPoolSizes;
} VkDescriptorPoolCreateInfo;

typedef enum VkDescriptorPoolCreateFlagBits {
    VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001,
    VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT = 0x00000002,
    VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_EXT = 0x00000004,
    VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
    VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_VALVE = VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_EXT,
    VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkDescriptorPoolCreateFlagBits;
typedef VkFlags VkDescriptorPoolCreateFlags;
typedef VkFlags VkDescriptorPoolResetFlags;

创建描述符集的资源

    在描述符集创建之前,我们必须首先创建与它绑定的资源对象。我们将创建一个一致变量缓存资源,之后将它与描述符集进行关联。

    在现在的示例程序中,我们通过 VulkanDrawable 类实现上面的函数接口,从而创建一个一致变量缓存并向其中保存一个 4×4 的变换矩阵。为此,我们需要首先创建缓存类型的资源。注意,Vulkan 中的资源有两种类型:缓存和图像。

创建描述符集

    描述符集的创建主要包括两个步骤:

  1. 描述符集的分配:从描述符池中分配新的描述符集。
  2. 资源的关联:将描述符集关联到刚创建的资源数据。

vkAllocateDescriptorSets —— 从描述符池中分配新的描述符集

   描述符集是通过 API 函数 vkAllocateDescriptorSets() 从描述符池中分配得到的。这个函数需要三个输入参数:第一个参数 device 设置了描述符池所在的逻辑设备(类型为 VkDevice);第二个参数 pAllocateInfo 是结构体 VkDescriptorSetAllocateInfo 的对象指针,它记录了从描述符池中进行分配所需的各种参数;最后一个参数 pDescriptorSets 是 VkDescriptorSet 数组的指针,它返回了分配得到的各个描述符集的句柄

VKAPI_ATTR VkResult VKAPI_CALL vkAllocateDescriptorSets(
    VkDevice                                    device,
    const VkDescriptorSetAllocateInfo*          pAllocateInfo,
    VkDescriptorSet*                            pDescriptorSets);
  1. VkDescriptorSetAllocateInfo
typedef struct VkDescriptorSetAllocateInfo {
    VkStructureType                 sType;
    const void*                     pNext;
    VkDescriptorPool                descriptorPool;
    uint32_t                        descriptorSetCount;
    const VkDescriptorSetLayout*    pSetLayouts;
} VkDescriptorSetAllocateInfo;

vkFreeDescriptorSets —— 从描述符池中分配新的描述符集

VKAPI_ATTR VkResult VKAPI_CALL vkFreeDescriptorSets(
    VkDevice                                    device,
    VkDescriptorPool                            descriptorPool,
    uint32_t                                    descriptorSetCount,
    const VkDescriptorSet*                      pDescriptorSets);

vkUpdateDescriptorSets —— 关联资源与描述符集

   描述符集可以与资源信息直接进行关联,并且通过 API 函数 vkUpdateDescriptorSets() 进行更新。这个函数有四个参数:第一个参数 device 是负责更新描述符集的逻辑设备。这个逻辑设备同时也应当是拥有这些描述符集的设备;第二个参数 descriptorWriteCount 设置了 pDescriptorWrites 数组(类型为 VkCopyDescriptorSet )中的元素个数。第三个参数 pDescriptorWrites 是一个 VkWriteDescriptorSet 对象数组的指针;第四个参数 descriptorCopyCount 设置了 pDescriptorCopies 数组中的元素个数;最后一个参数 pDescriptorCopies 设置的是我们需要复制的 VkCopyDescriptorSet 数组对象的指针。

VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSets(
    VkDevice                                    device,
    uint32_t                                    descriptorWriteCount,
    const VkWriteDescriptorSet*                 pDescriptorWrites,
    uint32_t                                    descriptorCopyCount,
    const VkCopyDescriptorSet*                  pDescriptorCopies);

 整个更新的过程包括了两类操作,也就是写入和复制操作。


写入:分配后的描述符集的更新可以通过填充零个或多个 VkWriteDescriptorSet 结构体对象组成的元素数组的方式来完成,其中包含了资源相关的信息,包括缓存数据、绑定索引等。写入的操作是在 API 函数 vkUpdateDescriptorSets() 中完成的。这个函数直接将填充后的 VkWriteDescriptorSet 结构体对象作为参数传入。


复制:复制操作需要用到目前已有的描述符集,并且将它们的信息复制到目标描述符集中。复制操作是通过结构体 VkCopyDescriptorSet 来具体设置的。我们同样可能用到零个或者多个复制操作的结构体对象。

VkWriteDescriptorSet

typedef struct VkWriteDescriptorSet {
    VkStructureType                  sType;
    const void*                      pNext;
    // 设置更新对应的目标描述符集
    VkDescriptorSet                  dstSet;
    /// 设置集合内的描述符绑定参数
    uint32_t                         dstBinding;
    // 设置描述符数组中的起始元素索引
    uint32_t                         dstArrayElement;
    // 设置要更新的描述符的数量,如果设置了以下参数的话:pImageInfo、pBufferInfo
    // 或 pTexelBufferView
    uint32_t                         descriptorCount;
    // 设置每个参与计算的描述符的类型(pImageInfo、pBufferInfo或 pTexelBufferView)
    VkDescriptorType                 descriptorType;
    // 设置一个 VkDescriptorImageInfo 结构体数组,表示图像资源
    const VkDescriptorImageInfo*     pImageInfo;
    const VkDescriptorBufferInfo*    pBufferInfo;
    const VkBufferView*              pTexelBufferView;
} VkWriteDescriptorSet;

VkCopyDescriptorSet

typedef struct VkCopyDescriptorSet {
    VkStructureType    sType;
    const void*        pNext;
    // 设置复制操作的源描述符集数组
    VkDescriptorSet    srcSet;
    // 设置源描述符集的绑定索引
    uint32_t           srcBinding;
    // 设置源描述符集数组中的起始元素索引
    uint32_t           srcArrayElement;
    // 设置复制操作的目标描述符集数组
    VkDescriptorSet    dstSet;
    // 设置目标描述符集的绑定索引
    uint32_t           dstBinding;
    // 设置目标描述符集数组中的起始元素索引
    uint32_t           dstArrayElement;
    // 设置准备从源复制到目标数组中的描述符集总数
    uint32_t           descriptorCount;
} VkCopyDescriptorSet;

vkCmdPushConstants —— 更新资源数据

VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants(
    VkCommandBuffer                             commandBuffer,
    VkPipelineLayout                            layout,
    VkShaderStageFlags                          stageFlags,
    uint32_t                                    offset,
    uint32_t                                    size,
    const void*                                 pValues);

示例:

  unsigned pushConstants[2] = {};
  pushConstants[0]      = constColorRGBFlag;
  memcpy(&pushConstants[1], &mixerValue, sizeof(float));
  vkCmdPushConstants(cmdPushConstant, drawableObj->pipelineLayout, 
      VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushConstants), pushConstants);

纹理

vkGetImageSubresourceLayout —— 查询资源的布局信息

VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout(
    VkDevice                                    device,
    VkImage                                     image,
    const VkImageSubresource*                   pSubresource,
    VkSubresourceLayout*                        pLayout);
  1. VkImageSubresource
typedef struct VkImageSubresource {
    VkImageAspectFlags    aspectMask;
    uint32_t              mipLevel;
    uint32_t              arrayLayer;
} VkImageSubresource;

vkCreateSampler —— 创建一个图像采样器

   采样器是一个包含了一系列算法的对象,这些算法用来控制格式化图像数据的显示方法,其中包含了多种不同的参数。这些参数可以用来控制图像的变换、缩小和放大滤波、mipmap 分级、边界裁切方式,并最终生成图像纹素的采样结果。


   Vulkan 中的图像采样器创建需要用到 API 函数 vkCreateSampler() 。这个 API 函数支持四个输入参数:第一个参数 device 是创建采样器对象所用的逻辑设备;第二个参数是图像属性的控制结构体,类型为 VkSamplerCreateInfo,在最后一个参数 pSampler 是图像采样器;第三个参数 pAllocator 负责控制宿主机内存的分配;这个函数将创建并返回一个采样器对象。

VKAPI_ATTR VkResult VKAPI_CALL vkCreateSampler(
    VkDevice                                    device,
    const VkSamplerCreateInfo*                  pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSampler*                                  pSampler);
  1. VkSamplerCreateInfo
typedef struct VkSamplerCreateInfo {
    VkStructureType         sType;
    const void*             pNext;
    VkSamplerCreateFlags    flags;
    VkFilter                magFilter;    // 放大滤波
    VkFilter                minFilter;    // 缩小滤波
    VkSamplerMipmapMode     mipmapMode;   // 滤波模式,线性模式、临近模式
    // 当纹理坐标超过了[0..1]范围的时候,这个参数控制图像在U坐标轴上的截取方式
    VkSamplerAddressMode    addressModeU;
    // 当纹理坐标超过了[0..1]范围的时候,这个参数控制图像在V坐标轴上的截取方式
    VkSamplerAddressMode    addressModeV;
    // 当纹理坐标超过了[0..1]范围的时候,这个参数控制图像在W坐标轴上的截取方式
    VkSamplerAddressMode    addressModeW;
    float                   mipLodBias;   // 浮点数偏移量
    VkBool32                anisotropyEnable; // 控制各向异性滤波是否开启
    float                   maxAnisotropy;    // 截取时的最大各向异性滤波值
    // 控制滤波查找过程中是否要和一个参考值进行比较计算
    VkBool32                compareEnable;    
    VkCompareOp             compareOp;    // 比较函数
    float                   minLod;     // 设置 LOD 计算过程中所使用的最小截取值
    float                   maxLod;     // 设置 LOD 计算过程中所使用的最大截取值
    VkBorderColor           borderColor;  // 预定义颜色
    VkBool32                unnormalizedCoordinates;  // 控制否是使用归一化的纹素坐标查找
} VkSamplerCreateInfo;
  • VkSamplerCreateFlags
typedef enum VkSamplerCreateFlagBits {
    VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT = 0x00000001,
    VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT = 0x00000002,
    VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT = 0x00000004,
    VK_SAMPLER_CREATE_IMAGE_PROCESSING_BIT_QCOM = 0x00000010,
    VK_SAMPLER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkSamplerCreateFlagBits;
typedef VkFlags VkSamplerCreateFlags;
  • VkFilter
typedef enum VkFilter {
    VK_FILTER_NEAREST = 0,
    VK_FILTER_LINEAR = 1,
    VK_FILTER_CUBIC_EXT = 1000015000,
    VK_FILTER_CUBIC_IMG = VK_FILTER_CUBIC_EXT,
    VK_FILTER_MAX_ENUM = 0x7FFFFFFF
} VkFilter;
  • VkSamplerMipmapMode
typedef enum VkSamplerMipmapMode {
    VK_SAMPLER_MIPMAP_MODE_NEAREST = 0,
    VK_SAMPLER_MIPMAP_MODE_LINEAR = 1,
    VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF
} VkSamplerMipmapMode;
  • VkSamplerAddressMode
typedef enum VkSamplerAddressMode {
    VK_SAMPLER_ADDRESS_MODE_REPEAT = 0,
    VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1,
    VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2,
    VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3,
    VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4,
    VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE_KHR = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE,
    VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF
} VkSamplerAddressMode;
  • VkCompareOp
typedef enum VkCompareOp {
    VK_COMPARE_OP_NEVER = 0,
    VK_COMPARE_OP_LESS = 1,
    VK_COMPARE_OP_EQUAL = 2,
    VK_COMPARE_OP_LESS_OR_EQUAL = 3,
    VK_COMPARE_OP_GREATER = 4,
    VK_COMPARE_OP_NOT_EQUAL = 5,
    VK_COMPARE_OP_GREATER_OR_EQUAL = 6,
    VK_COMPARE_OP_ALWAYS = 7,
    VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF
} VkCompareOp;
  • VkBorderColor
typedef enum VkBorderColor {
    VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0,
    VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1,
    VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2,
    VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3,
    VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4,
    VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5,
    VK_BORDER_COLOR_FLOAT_CUSTOM_EXT = 1000287003,
    VK_BORDER_COLOR_INT_CUSTOM_EXT = 1000287004,
    VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF
} VkBorderColor;
  1. VkAllocationCallbacks
typedef struct VkAllocationCallbacks {
    void*                                   pUserData;
    PFN_vkAllocationFunction                pfnAllocation;
    PFN_vkReallocationFunction              pfnReallocation;
    PFN_vkFreeFunction                      pfnFree;
    PFN_vkInternalAllocationNotification    pfnInternalAllocation;
    PFN_vkInternalFreeNotification          pfnInternalFree;
} VkAllocationCallbacks;


滤波

   当纹理被放大或者缩小的时候,纹理滤波方式可以控制纹理显示的质量。在某个深度值上,一个纹素可能正好等于屏幕上一个像素的大小。但是,如果我们贴敷一个较小的纹理到较大的几何体上,那么纹理将被拉伸。这一过程被称作放大。换句话说,这个时候图像的尺寸小于被映射的几何体的尺寸。反之,如果几何体尺寸小于图像的尺寸,那么多个纹素将共享同一个像素,此时显示的图像将被压缩,称作缩小。


   放大和缩小的图像效果可以通过滤波模式参数来设置。Vulkan 中使用 VkFilter 枚举量来完成这一操作。这个枚举量的两个取值如下所示:


VK_FILTER_NEAREST:采样器将使用给定的纹理坐标附近最近的纹素值。


VK_FILTER_LINEAR:采样器将计算给定纹理坐标附近四个最近的像素的加权平均值。

边界截取方式

   如果纹理映射的 U、V、W 坐标超出了 1.0,那么 Vulkan 支持多种采样寻址模式,类型为 VkSamplerAddressMode。Vulkan 采样时可以支持下面几种不同的边界截取寻址方式:


VK_SAMPLER_ADDRESS_MODE_REPEAT:产生重复的花纹。

VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:产生重复的花纹,邻接的纹素采用镜像的方式。

VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:产生重复的边界纹素直到边缘位置;

VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:直接截断边界之外的纹素。

如图11-2所示,我们设置纹理坐标范围比[0…1]更大,并且演示

了几种不同的截取方式的执行结果。

vkCmdCopyBuffer —— 数据内容从源缓存对象复制到目标缓存对象的设备内存中

VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer(
    VkCommandBuffer                             commandBuffer,
    VkBuffer                                    srcBuffer,
    VkBuffer                                    dstBuffer,
    uint32_t                                    regionCount,
    const VkBufferCopy*                         pRegions);

vkCmdCopyImage —— 源图像对象的一部分复制到目标图像区域中

VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage(
    VkCommandBuffer                             commandBuffer,
    VkImage                                     srcImage,
    VkImageLayout                               srcImageLayout,
    VkImage                                     dstImage,
    VkImageLayout                               dstImageLayout,
    uint32_t                                    regionCount,
    const VkImageCopy*                          pRegions);

vkCmdCopyBufferToImage —— 缓存对象的数据内容被复制到图像对象中

VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage(
  // 指令缓存对象
    VkCommandBuffer                             commandBuffer,  
    // 数据内容复制的数据源
    VkBuffer                                    srcBuffer,    
    // 数据内容将被复制到其中
    VkImage                                     dstImage, 
    // 设置 dstImage 对象的图像布局对象
    VkImageLayout                               dstImageLayout,
    // 设置传输数据内容时要复制的区域的总数
    uint32_t                                    regionCount,
    // 数组指针,包含了所有需要复制数据的区域信息
    const VkBufferImageCopy*                    pRegions);
  1. 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_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4,
    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5,
    VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6,
    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7,
    VK_IMAGE_LAYOUT_PREINITIALIZED = 8,
  ...
    VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF
} VkImageLayout;


  1. VkBufferImageCopy
typedef struct VkBufferImageCopy {
    VkDeviceSize                bufferOffset;
    uint32_t                    bufferRowLength;
    uint32_t                    bufferImageHeight;
    VkImageSubresourceLayers    imageSubresource;
    VkOffset3D                  imageOffset;
    VkExtent3D                  imageExtent;
} VkBufferImageCopy;
  • VkImageSubresourceLayers
typedef struct VkImageSubresourceLayers {
    VkImageAspectFlags    aspectMask;
    uint32_t              mipLevel;
    uint32_t              baseArrayLayer;
    uint32_t              layerCount;
} VkImageSubresourceLayers;


  • VkOffset3D
typedef struct VkOffset3D {
    int32_t    x;
    int32_t    y;
    int32_t    z;
} VkOffset3D;
  • VkExtent3D
 typedef struct VkExtent3D {
    uint32_t    width;
    uint32_t    height;
    uint32_t    depth;
} VkExtent3D;

vkCmdCopyImageToBuffer —— 图像对象的数据内容被复制到缓存对象中

VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer(
    VkCommandBuffer                             commandBuffer,
    VkImage                                     srcImage,
    VkImageLayout                               srcImageLayout,
    VkBuffer                                    dstBuffer,
    uint32_t                                    regionCount,
    const VkBufferImageCopy*                    pRegions);

目录
相关文章
|
22天前
|
存储 缓存 图形学
Vulkan 围炉夜话3
Vulkan 围炉夜话
26 9
Vulkan 围炉夜话3
|
22天前
|
缓存 API 开发工具
Vulkan 围炉夜话1
Vulkan 围炉夜话
30 15
|
22天前
|
缓存 并行计算 算法
Vulkan 围炉夜话2
Vulkan 围炉夜话
31 13
|
22天前
|
存储 缓存 API
Vulkan 围炉夜话4
Vulkan 围炉夜话
25 5
|
22天前
|
Unix API 图形学
OpenGL 围炉夜话
OpenGL 围炉夜话
34 10
|
22天前
|
Linux
BuildRoot 围炉夜话
BuildRoot 围炉夜话
63 9
|
22天前
|
Java 虚拟化 数据安全/隐私保护
MacOs 围炉夜话
MacOs 围炉夜话
55 10
|
存储 监控 JavaScript
保熟的TS知识,拜托,超快超酷的好吗
这一步对于很多人来说是最简单的一步,也是最难的一步,说简单是因为这确确实实仅是入门的一步,就是一个环境配置,说难则是因为很多人无法跨出这一步,当你跨出这一步之后,你会发现后面的真的学得很快很快,现在,就让我们一起跨出这一步吧~
55 0
|
JSON 算法 JavaScript
喜大普奔,es2019登场
就在刚4个小时前,TC39将以下特性加入到了 ES2019 中。让我们来看看这些新的特性给我们带来了什么样的改变。
喜大普奔,es2019登场
|
移动开发 监控 前端开发
SegmentFault 技术周刊 Vol.35 - WebGL:打开网页看大片
SegmentFault 技术周刊 Vol.35 - WebGL:打开网页看大片
247 0