Vulkan 围炉夜话2

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Vulkan 围炉夜话

Vulkan 围炉夜话1:https://developer.aliyun.com/article/1598108

vkAllocateMemory —— 分配设备内存
VKAPI_ATTR VkResult VKAPI_CALL vkAllocateMemory(
    VkDevice                                    device,
    const VkMemoryAllocateInfo*                 pAllocateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDeviceMemory*                             pMemory);
  1. VkMemoryAllocateInfo
typedef struct VkMemoryAllocateInfo {
    VkStructureType    sType;
    const void*        pNext;
    // 这个参数表示需要分配的内存大小
    VkDeviceSize       allocationSize;    
    // 这个参数设置了内存类型的索引号,可以用来设置内存所在的堆以及内存类型
    uint32_t           memoryTypeIndex;   
} VkMemoryAllocateInfo;
vkBindImageMemory —— 内存绑定图像资源

    vkBindImageMemory 函数把分配的内存 VkDeviceMemory 绑定给 VkImage 使用

VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory(
    VkDevice                                    device,
    // 设置了需要绑定到内存的 VkImage 对象
    VkImage                                     image,
    // 设置了分配的 VkDeviceMemory 内存对象
    VkDeviceMemory                              memory,
    // 设置了图像绑定到内存的起点偏移地址,按字节数计算
    VkDeviceSize                                memoryOffset);
vkCreateImageView—— 创建图像资源视图

    主机端要使用 VkImageView 来操作 VkImage,使用 vkCreateImageView 对两者进行关联

VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView(
    VkDevice                                    device,
    const VkImageViewCreateInfo*                pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkImageView*                                pView);
  1. VkImageViewCreateInfo
typedef struct VkImageViewCreateInfo {
    VkStructureType            sType;
    const void*                pNext;
    VkImageViewCreateFlags     flags;
    VkImage                    image;
    VkImageViewType            viewType;
    VkFormat                   format;
    VkComponentMapping         components;
    VkImageSubresourceRange    subresourceRange;
} VkImageViewCreateInfo;
  • VkImageViewCreateFlagBits
typedef enum VkImageViewCreateFlagBits {
    VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT = 0x00000001,
    VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DEFERRED_BIT_EXT = 0x00000002,
    VK_IMAGE_VIEW_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkImageViewCreateFlagBits;
typedef VkFlags VkImageViewCreateFlags;
typedef VkFlags VkShaderModuleCreateFlags;
  • VkImageViewType
typedef enum VkImageViewType {
    VK_IMAGE_VIEW_TYPE_1D = 0,
    VK_IMAGE_VIEW_TYPE_2D = 1,
    VK_IMAGE_VIEW_TYPE_3D = 2,
    VK_IMAGE_VIEW_TYPE_CUBE = 3,
    VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4,
    VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5,
    VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6,
    VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF
} VkImageViewType;
  • VkComponentMapping
typedef struct VkComponentMapping {
    VkComponentSwizzle    r;
    VkComponentSwizzle    g;
    VkComponentSwizzle    b;
    VkComponentSwizzle    a;
} VkComponentMapping;
  • VkImageSubresourceRange
typedef struct VkImageSubresourceRange {
    VkImageAspectFlags    aspectMask;
    uint32_t              baseMipLevel;
    uint32_t              levelCount;
    uint32_t              baseArrayLayer;
    uint32_t              layerCount;
} VkImageSubresourceRange;

typedef enum VkImageAspectFlagBits {
    VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001,
    VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002,
    VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004,
    VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008,
  ...
    VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkImageAspectFlagBits;
typedef VkFlags VkImageAspectFlags;

VkBuffer 相关

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

vkCreateBuffer—— 创建缓存资源
VKAPI_ATTR VkResult VKAPI_CALL vkCreateBuffer(
    VkDevice                                    device,
    const VkBufferCreateInfo*                   pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkBuffer*                                   pBuffer);
  1. VkBufferCreateInfo
typedef struct VkBufferCreateInfo {
    VkStructureType        sType;
    const void*            pNext;
    VkBufferCreateFlags    flags;
    VkDeviceSize           size;    // 创建的缓存总大小,单位字节
    VkBufferUsageFlags     usage;   // 缓存资源用途
    VkSharingMode          sharingMode; // 图像在多个队列族之间的共享模式
    uint32_t               queueFamilyIndexCount; // 数组 pQueueFamilyIndices 的元素个数
    const uint32_t*        pQueueFamilyIndices;   // 准备访问缓存的队列族的数组
} VkBufferCreateInfo;
  • VkBufferCreateFlags
typedef enum VkBufferCreateFlagBits {
    VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001,
    VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002,
    VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004,
    VK_BUFFER_CREATE_PROTECTED_BIT = 0x00000008,
    VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 0x00000010,
    VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_EXT = VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT,
    VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR = VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT,
    VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkBufferCreateFlagBits;
typedef VkFlags VkBufferCreateFlags;
  • VkBufferUsageFlagBits
typedef enum VkBufferUsageFlagBits {
    VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 0x00000001,
    VK_BUFFER_USAGE_TRANSFER_DST_BIT = 0x00000002,
    VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000004,
    VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 0x00000008,
    VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 0x00000010,
    VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 0x00000020,
    VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040,
    VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080,
    VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100,
    VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT = 0x00020000,
  ...
    VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkBufferUsageFlagBits;
typedef VkFlags VkBufferUsageFlags;
typedef VkFlags VkBufferViewCreateFlags;
示例
  VkBufferCreateInfo bufInfo = {};
  bufInfo.sType         = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  bufInfo.pNext         = NULL;
  bufInfo.usage         = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
  bufInfo.size          = sizeof(MVP);
  bufInfo.queueFamilyIndexCount = 0;
  bufInfo.pQueueFamilyIndices = NULL;
  bufInfo.sharingMode     = VK_SHARING_MODE_EXCLUSIVE;
  bufInfo.flags         = 0;

  // Use create buffer info and create the buffer objects
  result = vkCreateBuffer(deviceObj->device, &bufInfo, NULL, &UniformData.buffer);
vkBindBufferMemory —— 内存绑定缓存资源

    vkBindBufferMemory 函数把分配的内存 VkDeviceMemory 绑定给 VkBuffer 使用

VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory(
    VkDevice                                    device,
    VkBuffer                                    buffer,
    VkDeviceMemory                              memory,
    VkDeviceSize                                memoryOffset);
vkCreateBufferView —— 创建缓存资源视图

    可选择使用 vkCreateBufferViewVkBufferVkBufferView 进行关联

VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferView(
    VkDevice                                    device,
    const VkBufferViewCreateInfo*               pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkBufferView*                               pView);
  1. VkBufferViewCreateInfo
typedef struct VkBufferViewCreateInfo {
    VkStructureType            sType;
    const void*                pNext;
    VkBufferViewCreateFlags    flags;
    VkBuffer                   buffer;
    VkFormat                   format;
    // 如果我们将颜色、深度、模板转换为颜色分量,这个参数用来设置重映射的偏移量
    VkDeviceSize               offset;
    // 用来选择一个 mipmap 和纹理数组的层级范围,确保它们对视图可见
    VkDeviceSize               range;
} VkBufferViewCreateInfo;


VkDeviceMemory 映射

vkMapMemory —— 映射到主机空间
VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory(
    VkDevice                                    device,
    VkDeviceMemory                              memory,
    VkDeviceSize                                offset,
    VkDeviceSize                                size,
    VkMemoryMapFlags                            flags,
    void**                                      ppData);
vkUnmapMemory —— 解除映射
VKAPI_ATTR void VKAPI_CALL vkUnmapMemory(
    VkDevice                                    device,
    VkDeviceMemory                              memory);

理解代码流线

   我们现在详细地理解一下之前的实现过程。首先创建一个 VkCreateBufferInfo 结构体,用顶点缓存的元数据填充它的内容。我们将缓存的用途类型保存在这里,用来设置顶点的信息(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)。其他用途类型可以是针对索引缓存、uniform 缓存、纹理缓存,等等。我们需要设置顶点缓存的大小(按字节)。因为并没有设置针对多队列的需求,我们可以设置共享模式为 VK_SHARING_MODE_EXCLUSIVE 。将结构体传递给 API 函数 vkCreateBuffer() ,创建顶点缓存对象(VertexBuffer::buf)。


   我们将创建的缓存对象(VertexBuffer::buf)作为参数传递给函数vkGetBufferMemoryRequirements() ,从而获取缓存分配的内存需求信息(memRqrmnt)。这个信息可以帮助系统按照合适的内存大小来分配缓存资源所需的空间。这个函数还需要用到结构体 VkCreateBufferInfo 。


   下一步,我们准备进行空间分配,根据之前的需求信息创建一个 VkMemoryAllocateInfo 对象(allocinfo)。我们设置分配的字节大小并且获取兼容的内存类型。内存的分配需要用到 vkAllocateMemory 函数,输入参数为 allocinfo,获得的设备内存类型为 VkDeviceMemory,保存在 VertexBuffer.mem 变量中。


   完成物理内存的分配之后,使用 vkMapMemory() 将它映射到宿主机本地内存,这样我们就可以直接将几何体数据复制到物理设备内存当中。我们将数据复制到物理设备内存之后,别忘了使用 vkUnmapMemory() 执行解除映射的操作。


   最后我们通过 vkBindBufferMemory() 函数将设备内存(VkDeviceMemory)和缓存对象(VkBuffer)相互绑定在一起。

渲染通道

理解渲染通道

   渲染通道(Render Pass)设置了准备在渲染时用到的帧缓存附件和子通道。附件,例如颜色和深度,设置了当前颜色和深度图像的数量。它设置了渲染过程中每幅图像所需使用的采样位数,以及图像内容的使用方法。它还设置了每个渲染通道实例的开始和结束部分如何处理图像数据。在指令缓存中使用的渲染通道被称作渲染通道实例。它负责管理子通道之间的依赖关系,定义附件与子通道之间的协议关系。


   渲染通道中主要包括两大部分:附件和子通道。以下针对附件和子通道进行进一步的讲解。


附件

   附件表示执行渲染指令时用到的一块表面区域(例如颜色、深度/模板,或者执行解析操作的解析附件)。附件的类型主要有以下五种:

颜色附件(color attachment):颜色附件表示渲染图元数据时用到的绘制目标图像。

深度附件(depth attachment):深度附件负责保存深度信息,并执行深度/模板测试操作。

解析附件(resolve attachment):解析附件在子通道的末尾会自动从一个多重采样的附件降维到一个单一采样的附件。解析附件与多重采用的颜色附件对应,并且在子通道的末尾从颜色附件转换到对应的解析附件,即 vkCmdResolveImage。有一个例外情况是,有时候驱动程序可能会采用更优化的工作方式,例如同时执行溢出和解析操作。

输入附件(input attachment):这里包含了一组附件并与着色器对象共享使用。输入附件有点类似一个严格定义的纹理,而着色器中执行的唯一操作就是纹素的获取(texture(tex,uv))——即按照着色器当前像素位置去读取相应的纹素数据。这里有一个理所当然的应用就是在经典的延迟渲染过程中,从 G-buffer 读取数据并进行后处理的滤波算法(无模糊等),以及光照处理等操作。

保留附件(preserve attachment):在一个给定的子通道中,保留附件中的内容会始终保持不变。保留附件并不会在其他 API 中使用,它们唯一的作用是暂时保持某些附件对应的内容在当前子通道中不变(之后可能会使用)。对于桌面系统 GPU 来说这个功能可能没有什么用,因为渲染目标是直接写入到内存的。但是,对于其他系统来说,某一部分保存在芯片内存上的附件数据可能需要在某个子通道中被其他附件复用,此时不需要将附件内容重新放回到内存中。

子通道

   渲染通道中的子通道负责读取和写入到对应的附件中。当前渲染通道中的子通道的执行可以通过下面的渲染指令来完成:

子通道可以从之前写入的附件(必须是保留附件)读取,然后写入到当前关联的附件。

在渲染通道的实例中,子通道的附件也写入到颜色、深度和模板缓存。

如果要在后续的通道中使用当前的子通道附件,应用程序需要负责确保子通道信息在使用过程中始终有效。

保留附件在子通道的生命周期当中可以始终保持附件内容不变。此时子通道不会影响到被保护的附件的读写操作。换句话说,在子通道的生命周期当中,附件无法被读取或者写入。

创建渲染通道

  1. vkCreateRenderPass
VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass(
    VkDevice                                    device,
    const VkRenderPassCreateInfo*               pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkRenderPass*                               pRenderPass);
  1. VkRenderPassCreateInfo
typedef struct VkRenderPassCreateInfo {
    VkStructureType                   sType;
    const void*                       pNext;
    VkRenderPassCreateFlags           flags;
    uint32_t                          attachmentCount;
    const VkAttachmentDescription*    pAttachments;
    uint32_t                          subpassCount;
    const VkSubpassDescription*       pSubpasses;
    uint32_t                          dependencyCount;
    const VkSubpassDependency*        pDependencies;
} VkRenderPassCreateInfo;
  1. VkAttachmentDescription
typedef struct VkAttachmentDescription {
    VkAttachmentDescriptionFlags    flags;
    VkFormat                        format;
    VkSampleCountFlagBits           samples;
    VkAttachmentLoadOp              loadOp;
    VkAttachmentStoreOp             storeOp;
    VkAttachmentLoadOp              stencilLoadOp;
    VkAttachmentStoreOp             stencilStoreOp;
    VkImageLayout                   initialLayout;
    VkImageLayout                   finalLayout;
} VkAttachmentDescription;
  1. VkSubpassDescription
typedef struct VkSubpassDescription {
    VkSubpassDescriptionFlags       flags;
    VkPipelineBindPoint             pipelineBindPoint;
    uint32_t                        inputAttachmentCount;
    const VkAttachmentReference*    pInputAttachments;
    uint32_t                        colorAttachmentCount;
    const VkAttachmentReference*    pColorAttachments;
    const VkAttachmentReference*    pResolveAttachments;
    const VkAttachmentReference*    pDepthStencilAttachment;
    uint32_t                        preserveAttachmentCount;
    const uint32_t*                 pPreserveAttachments;
} VkSubpassDescription;
  1. VkSubpassDependency
typedef struct VkSubpassDependency {
    uint32_t                srcSubpass;
    uint32_t                dstSubpass;
    VkPipelineStageFlags    srcStageMask;
    VkPipelineStageFlags    dstStageMask;
    VkAccessFlags           srcAccessMask;
    VkAccessFlags           dstAccessMask;
    VkDependencyFlags       dependencyFlags;
} VkSubpassDependency;
  1. VkAttachmentReference
typedef struct VkAttachmentReference {
    uint32_t         attachment;
    VkImageLayout    layout;
} VkAttachmentReference;

VkFramebuffer 帧缓存

vkCreateFramebuffer —— 创建帧缓存

VKAPI_ATTR VkResult VKAPI_CALL vkCreateFramebuffer(
    VkDevice                                    device,
    const VkFramebufferCreateInfo*              pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkFramebuffer*                              pFramebuffer);
  1. VkFramebufferCreateInfo
typedef struct VkFramebufferCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkFramebufferCreateFlags    flags;
    VkRenderPass                renderPass;
    uint32_t                    attachmentCount;
    const VkImageView*          pAttachments;
    uint32_t                    width;
    uint32_t                    height;
    uint32_t                    layers;
} VkFramebufferCreateInfo;
  • VkFramebufferCreateFlags
typedef enum VkFramebufferCreateFlagBits {
    VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT = 0x00000001,
    VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT,
    VK_FRAMEBUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkFramebufferCreateFlagBits;
typedef VkFlags VkFramebufferCreateFlags;
示例
  VkFramebufferCreateInfo fbInfo  = {};
  fbInfo.sType          = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
  fbInfo.pNext          = NULL;
  fbInfo.renderPass       = renderPass;
  fbInfo.attachmentCount      = includeDepth ? 2 : 1;
  fbInfo.pAttachments       = attachments;
  fbInfo.width          = width;
  fbInfo.height         = height;
  fbInfo.layers         = 1;

  uint32_t i;

  framebuffers.clear();
  framebuffers.resize(swapChainObj->scPublicVars.swapchainImageCount);
  for (i = 0; i < swapChainObj->scPublicVars.swapchainImageCount; i++) {
    attachments[0] = swapChainObj->scPublicVars.colorBuffer[i].view;
    result = vkCreateFramebuffer(deviceObj->device, &fbInfo, NULL, &framebuffers.at(i));
    assert(result == VK_SUCCESS);
  }


流水线

  流水线指的是由一系列固定阶段组成,数据流输入之后,每一个阶段对数据进行处理之后,将它传递给下一个阶段。最终的成果可以是2D的光栅化之后的绘制图形(图形流水线),也可以是通过某种计算逻辑(计算流水线)完成更新的资源信息(缓存或者图像)。


   Vulkan 支持两种类型的流水线——图形流水线和计算流水线。


图形流水线(graphics pipeline):这个流水线会使用指令缓存中的一组 Vulkan 指令,将 2D/3D 场景绘制到 2D 光栅化图像上。

计算流水线(compute pipeline):这个流水线使用指令缓存中的一组 Vulkan 指令,来处理计算性的工作。

   整个流水线的执行是从输入装配开始,此时输入的顶点数据会被装配成点、线、或者三角形形式的图元拓扑结构。通过可编程的顶点着色器阶段,输入的顶点数据被转换到剪切空间。几何体通过细分控制着色器和细分计算着色器两个装配阶段进行细分,而几何着色器是唯一一个可以从单一的输入图元产生多个输出图元的阶段。


   然后,我们通过图元装配环节获取前面阶段中变换坐标后的所有顶点,并且将它们按照之前输入的指定绘制/图元类型(点、线、三角形)进行排列。如果输入的顶点坐标已经在当前视口区域之外,那么对应的图元会被裁切掉,此时被裁切的片元(视口之外)也会被直接丢弃。


   光栅化即变换到屏幕坐标空间的图元(点、线、三角形)转换为离散元素(即片元)的过程。片元会交由下一个阶段进行操作,即片元着色器。片元着色器对每个单独的片元执行运算。这些片元最终会成为帧缓存的一部分,并且会再度经历一系列可能的数据更新操作,比如深度测试、模板测试,以及片元融混。


   缓存和图像内存可以放在一个独立的流水线中以 1D/2D/3D 工作组的形式进行处理,即计算流水线。计算流水线对于各种并行计算的需求来说是非常强大的。在计算流水线中可以同时修改(读/写)缓存和图像的内存数据。


   流水线中通常包括了三大基本概念:流水线状态对象、流水线缓冲对象、流水线布局。它们可以高效地控制底层流水线的操作:


流水线状态对象(Pipeline State Object,PSO):物理设备或者 GPU 端可以直接在硬件层面执行一些特定类型的操作。这些操作包括光栅化和条件更新,比如融混、深度测试、模板化等。 Vulkan 允许我们通过 PSO 自由控制这些硬件属性设置。其他一些硬件端直接控制的操作还包括根据图元拓扑类型(点、线、三角形)进行给定几何体形状装配、视口的控制,等等。


流水线缓冲对象(Pipeline Cache Object,PCO):流水线缓冲的机制可以帮助我们快速获取合复用之前的流水线。应用程序可以因此更好地避免反复创建相同或者相似的流水线对象。


流水线布局(Pipeline layout):缓存和图像数据都是间接关联到着色器上的,可以通过着色器的资源变量进行访问。资源变量被关联到缓存和图像的视图对象。这些资源变量可以通过描述符和描述符的集合布局来进行管理。在流水线当中,流水线布局可以管理一系列的描述符集合布局。


vkCreatePipelineLayout,vkCreateGraphicsPipelines,vkCreateComputePipelines,vkCreatePipelineCache,vkMergePipelineCaches

创建流水线缓冲对象(PCO) —— vkCreatePipelineCache

VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineCache(
    VkDevice                                    device,
    const VkPipelineCacheCreateInfo*            pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkPipelineCache*                            pPipelineCache);
  1. VkPipelineCacheCreateInfo
typedef struct VkPipelineCacheCreateInfo {
    VkStructureType               sType;
    const void*                   pNext;
    VkPipelineCacheCreateFlags    flags;
    size_t                        initialDataSize;
    const void*                   pInitialData;
} VkPipelineCacheCreateInfo;

合并流水线缓冲 —— vkMergePipelineCaches

VKAPI_ATTR VkResult VKAPI_CALL vkMergePipelineCaches(
    VkDevice                                    device,
    VkPipelineCache                             dstCache,
    uint32_t                                    srcCacheCount,
    const VkPipelineCache*                      pSrcCaches);

从流水线缓冲获取数据 —— vkGetPipelineCacheData

VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineCacheData(
    VkDevice                                    device,
    VkPipelineCache                             pipelineCache,
    size_t*                                     pDataSize,
    void*                                       pData);


Vulkan 围炉夜话3: https://developer.aliyun.com/article/1598111

目录
相关文章
|
22天前
|
存储 缓存 图形学
Vulkan 围炉夜话3
Vulkan 围炉夜话
26 9
Vulkan 围炉夜话3
|
22天前
|
缓存 API 开发工具
Vulkan 围炉夜话1
Vulkan 围炉夜话
30 15
|
22天前
|
存储 缓存 API
Vulkan 围炉夜话4
Vulkan 围炉夜话
25 5
|
22天前
|
存储 缓存 算法
Vulkan 围炉夜话5
Vulkan 围炉夜话
17 4
|
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