Skip to content

Latest commit

 

History

History
1003 lines (715 loc) · 55.4 KB

Core.md

File metadata and controls

1003 lines (715 loc) · 55.4 KB

核心设计

更新日志

  • 2023/3/19

    • 更新Dynamic Rendering章节
    • 创建Device Feature章节
  • 2023/3/20

    • 创建更新Device Feature章节
  • 2023/3/22

    • 创建更新Dynamic Rendering的Pipeline章节
  • 2023/3/25

    • 更新Dynamic Rendering的Pipeline章节
  • 2023/3/29

    • 更新Dynamic Rendering的Pipeline章节
    • 创建Dynamic Rendering的渲染章节
  • 2023/4/10

    • 创建Tessellation章节
    • 创建Device Tessellation Feature章节
    • 创建Tessellation Graphics Pipeline章节
  • 2023/4/16

    • 创建Mesh Shader章节
    • 创建Mesh Shader 接口函数章节
    • 创建Mesh Shader Feature章节
  • 2023/4/17

    • 更新Mesh Shader章节
  • 2023/4/18

    • 创建获取 Vulkan API章节
    • 创建实例级别章节
    • 创建物理设备级别章节
    • 创建设备级别章节
    • 创建获取Vulkan版本章节
    • 创建是否支持Vulkan章节
  • 2023/4/19

    • 更新获取 Vulkan API章节
    • 更新物理设备级别章节
    • 更新设备级别章节
  • 2023/4/21

    • 更新Mesh Shader章节
    • 创建Vulkan标准和扩展章节
  • 2023/4/24

    • 创建Mesh Shader GraphicsPipeline章节
  • 2023/4/25

    • 更新Mesh Shader GraphicsPipeline章节
  • 2023/4/27

    • 创建Specialization Constants章节
  • 2023/5/2

    • 更新VkSpecializationInfo章节
  • 2023/5/4

    • 创建特化判断流程章节
  • 2023/5/6

    • 创建Ray Tracing章节
    • 创建历史回顾章节
    • 创建光追标准章节
  • 2023/5/7

    • 更新光追标准章节
    • 创建扩展结构章节
  • 2023/5/8

    • 更新扩展结构章节
    • 创建加速结构(Acceleration Structures)章节
    • 创建Host端的延迟操作(Deferred Host Operations)章节
  • 2023/5/9

    • 创建光追管线章节
    • 创建光线查询章节
    • 创建正路在此章节
    • 创建加速结构的创建章节
  • 2023/5/10

    • 更新加速结构的创建章节
    • 创建资源使用和同步章节
    • 创建结论和资源章节

获取 Vulkan API

由于Vulkan中有三种级别的函数

  • 实例级别(instance-level)的函数(使用vkGetInstanceProcAddr获取)
  • 物理设备级别(physical-device-level)的函数(使用vkGetInstanceProcAddr获取)
  • 设备级别(device-level)的函数(使用vkGetDeviceProcAddr获取)

随着Vulkan的发展版本的更新,会随着版本的更新增加新的函数,如果创建VkInstance时指定的版本和物理设备获取的函数的Vulkan发布版本(VkPhysicalDeviceProperties::apiVersion)早的话,就算返回了有效函数也不应该使用(但有例外,见下文物理设备级别章节),比如:创建VkInstance时指定的版本为Vulkan1.0而之后使用该实例去获取Vulkan1.1发布的vkBindBufferMemory2函数,此时就算返回的函数可用也不应该使用。

实例级别

实例级别的函数广义上是指Vulkan中的所有可使用VkInstanceVkPhysicalDeviceVkDeviceVkQueue或者VkCommandBuffer调度的函数,并且在获取这些函数时需要传递一个有效VkInstance

其中有一些函数比较特殊,为全局函数,在获取时不需要传递有效VkInstance,直接传递VK_NULL_HANDLE既可以有效获取:

  • vkEnumerateInstanceVersion
  • vkEnumerateInstanceExtensionProperties
  • vkEnumerateInstanceLayerProperties
  • vkCreateInstance

物理设备级别

物理设备级别的函数是指接口函数第一个参数是VkPhysicalDevice的函数,比如vkGetPhysicalDeviceProperties或者vkGetPhysicalDeviceFeatures等函数。

物理设备级别函数的获取有特例,可以获取Vulkan高版本的物理设备级别函数进行使用(前提是物理设备支持高版本的Vulkan)。比如:创建VkInstance时指定的版本为Vulkan1.0而之后使用该实例去获取Vulkan1.1发布的vkGetPhysicalDeviceFeatures2函数,如果设备支持高版本的Vulkan并且返回正常则可使用该函数,反之则不行

设备级别

设备级别的函数是指接口函数第一个参数是VkDeviceVkQueueVkCommandBuffer的函数,比如vkCmdDrawvkCreateBuffer等。

获取Vulkan版本

由于历史原因,在Vulkan1.0版本中获取不到系统支持的Vulkan版本,只能获取到显卡设备支持的Vulkan版本,后来在Vulkan1.1后增加了vkEnumerateInstanceVersion函数来获取Vulkan版本,而vkEnumerateInstanceVersion为全局函数,可能会返回nullptr(当系统只支持Vulkan1.0时)。所以需要有一个判断流程用于获取Vulkan版本

graph TD;
LoadVulkanLib("加载Vulkan动态库")
IsLoadVulkanLib{"是否加载"}
ReturnVersion000("返回版本号0.0.0")
GetvkEnumerateInstanceVersion("获取vkEnumerateInstanceVersion函数")
IsGetvkEnumerateInstanceVersion{"是否获取到"}
GetvkCreateInstance("获取vkCreateInstance函数")
IsGetvkCreateInstance{"是否获取到"}
IsCreateVkInstance{"是否成功创建VkInstance"}
GetvkDestroyInstance("获取vkDestroyInstance函数")
IsGetvkDestroyInstance{"是否获取到"}

LoadVulkanLib-->IsLoadVulkanLib
IsLoadVulkanLib--"加载成功(注:返回前卸载)"-->GetvkEnumerateInstanceVersion
IsLoadVulkanLib--加载失败-->ReturnVersion000

GetvkEnumerateInstanceVersion-->IsGetvkEnumerateInstanceVersion
IsGetvkEnumerateInstanceVersion--成功获取-->ReturnSupportVersion("返回支持的Vulkan版本")
IsGetvkEnumerateInstanceVersion--获取失败-->GetvkCreateInstance

GetvkCreateInstance-->IsGetvkCreateInstance
IsGetvkCreateInstance--成功获取-->TryToCreateVkInstance("尝试创建VkInstance1.0")
IsGetvkCreateInstance--获取失败-->ReturnVersion000

TryToCreateVkInstance-->IsCreateVkInstance
IsCreateVkInstance--成功创建-->GetvkDestroyInstance
IsCreateVkInstance--创建失败-->ReturnVersion000

GetvkDestroyInstance-->IsGetvkDestroyInstance
IsGetvkDestroyInstance--成功获取-->DestroyInstance("销毁之前创建的VkInstance")
IsGetvkDestroyInstance--获取失败-->ReturnException("返回一个异常")

DestroyInstance-->ReturnSupportVersion1.0("返回支持的Vulkan1.0")
Loading

是否支持Vulkan

通过获取Vulkan版本判断

graph TD;
GetVulkanVersion("获取Vulkan版本")
IfGetVersion000OrGetException{"返回版本0.0.0或者抛出异常"}


GetVulkanVersion-->IfGetVersion000OrGetException
IfGetVersion000OrGetException--是-->ReturnFalse("返回不支持Vulkan")
IfGetVersion000OrGetException--否-->ReturnTrue("返回支持Vulkan")
Loading

Vulkan标准和扩展

随着Vulkan的发展,每一个Vulkan核心版本都会发布新的扩展,并在下一个Vulkan核心版中将之前的一部分扩展提升为核心,而Turbo也需要随着Vulkan的发展进行适配。首要原则是能使用核心标准,尽量使用核心标准,如果设备支持的核心标准过低(导致高版本的Vulkan核心功能还处于扩展状态),尝试获取扩展(在用户开启对应扩展功能之后才去获取扩展功能,否则就算用户使用Turbo调用了扩展函数,Turbo也不会做任何扩展函数调用)。

如果低版本的Vulkan实例获取高版本的Vulkan核心API,所有的高版本的函数皆为nullptr,即使调用也不做任何事情。(注:Vulkan1.0版本的核心API一定不能为nullptr

graph TD;

ThrowException0("抛出异常")
ThrowException1("抛出异常")
Start(("Vulkan\n核心和扩展"))
IfSupportInVulkanCore{"Vulkan核心是否支持该功能\n(通过当前实例的版本)"}
UseVulkanCore("使用Vulkan核心")
GetCoreVulkanAPI("获取Vulkan核心API")
IsGetLegalVulkanCoreAPI{"是否获得合法Vulkan核心API指针"}
UseLegalVulkanCoreAPI("使用合法Vulkan核心API指针")

IfEnableExtension{"是否计划激活对应功能扩展"}
IfSupportExtension{"设备是否支持对应功能扩展"}
Donothing("什么都不做\n(此时对应的扩展函数指针为nullptr,Turbo在判断对应的函数指针为nullptr后直接跳过)")
GetExtensionVulkanAPI("获取Vulkan扩展API")
IsExtensionVulkanAPILegal{"是否获得合法Vulkan扩展API指针"}
UseLegalVulkanExtensionAPI("使用合法Vulkan扩展API指针")

Start-->IfSupportInVulkanCore
IfSupportInVulkanCore--支持-->UseVulkanCore-->GetCoreVulkanAPI-->IsGetLegalVulkanCoreAPI
IsGetLegalVulkanCoreAPI--不合法-->ThrowException0
IsGetLegalVulkanCoreAPI--合法-->UseLegalVulkanCoreAPI
IfSupportInVulkanCore--不支持-->IfEnableExtension

IfEnableExtension--未计划激活-->Donothing
IfEnableExtension--计划激活-->IfSupportExtension--支持-->GetExtensionVulkanAPI
IfSupportExtension--不支持-->Donothing

GetExtensionVulkanAPI-->IsExtensionVulkanAPILegal
IsExtensionVulkanAPILegal--不合法-->ThrowException1
IsExtensionVulkanAPILegal--合法-->UseLegalVulkanExtensionAPI
Loading

Device Feature

随着Vulkan标准的发展,每次新版本的发布都会带来新的特性,这就需要Turbo维护自己的特性,提供维护自己的TFeature数据

经常需要使用的特性如下:

  • geometryShaderVulkan1.0
  • tessellationShaderVulkan1.0
  • sampleRateShadingVulkan1.0
  • depthClampVulkan1.0
  • depthBiasClampVulkan1.0
  • wideLinesVulkan1.0
  • fillModeNonSolidVulkan1.0
  • timelineSemaphoreVulkan1.2
  • dynamicRenderingVulkan1.3

Vulkan中获取核心特性和扩展的特性需要通过vkGetPhysicalDeviceFeatures2或者vkGetPhysicalDeviceFeatures2KHR函数获取。

注意vkGetPhysicalDeviceFeatures2Vulkan1.1的函数

// Provided by VK_VERSION_1_1
void vkGetPhysicalDeviceFeatures2(
    VkPhysicalDevice                            physicalDevice,
    VkPhysicalDeviceFeatures2*                  pFeatures);
// Provided by VK_KHR_get_physical_device_properties2
void vkGetPhysicalDeviceFeatures2KHR(
    VkPhysicalDevice                            physicalDevice,
    VkPhysicalDeviceFeatures2*                  pFeatures);
// Provided by VK_VERSION_1_1
typedef struct VkPhysicalDeviceFeatures2 {
    VkStructureType             sType;
    void*                       pNext;
    VkPhysicalDeviceFeatures    features;
} VkPhysicalDeviceFeatures2;
// Provided by VK_KHR_get_physical_device_properties2
typedef VkPhysicalDeviceFeatures2 VkPhysicalDeviceFeatures2KHR;

如果想要使用vkGetPhysicalDeviceFeatures2KHR需要激活VK_KHR_get_physical_device_properties2Instance扩展

其中VkPhysicalDeviceFeatures2结构体除了可以在vkGetPhysicalDeviceFeatures2vkGetPhysicalDeviceFeatures2KHR中使用,也可以用于VkDeviceCreateInfopNext链中,相当于在创建VkDevice时通过pEnabledFeatures指定支持的特性。

对于Vulkan1.1的特性的获取是通过在vkGetPhysicalDeviceFeatures2函数中VkPhysicalDeviceFeatures2pNext中指定VkPhysicalDeviceVulkan11Features

VkDeviceCreateInfopNext链中指定VkPhysicalDeviceVulkan11Features,用于激活Vulkan1.1特性

注:VkPhysicalDeviceVulkan11FeaturesVulkan1.2中定义的

同获取Vulkan1.2的特性方式与获取Vulkan1.1特性一样,通过在vkGetPhysicalDeviceFeatures2函数中VkPhysicalDeviceFeatures2pNext中指定VkPhysicalDeviceVulkan12Features

VkDeviceCreateInfopNext链中指定VkPhysicalDeviceVulkan12Features,用于激活Vulkan1.2特性

获取Vulkan1.3的特性方式与之前相同,通过在vkGetPhysicalDeviceFeatures2函数中VkPhysicalDeviceFeatures2pNext中指定VkPhysicalDeviceVulkan13Features

VkDeviceCreateInfopNext链中指定VkPhysicalDeviceVulkan13Features,用于激活Vulkan1.3特性

Vulkan1.3版本中,对于DynamicRendering特性,除了VkBool32 VkPhysicalDeviceVulkan13Features::dynamicRendering中可以获取之外,还可以通过在vkGetPhysicalDeviceFeatures2函数中VkPhysicalDeviceFeatures2pNext中指定VkPhysicalDeviceDynamicRenderingFeatures获取

// Provided by VK_VERSION_1_3
typedef struct VkPhysicalDeviceDynamicRenderingFeatures {
    VkStructureType    sType;
    void*              pNext;
    VkBool32           dynamicRendering;
} VkPhysicalDeviceDynamicRenderingFeatures;

并且VkPhysicalDeviceDynamicRenderingFeatures也可以用于VkDeviceCreateInfopNext链中,用于激活DynamicRendering特性

注:按照Vulkan1.3的标准,如果VkDeviceCreateInfo::pNext中已经链上了VkPhysicalDeviceVulkan13Features则不能再链接VkPhysicalDeviceDynamicRenderingFeatures

Dynamic Rendering

对于Vulkan中的Dynamic Rendering主要如下函数

  • vkCmdBeginRenderingKHR或者vkCmdBeginRendering
  • vkCmdEndRenderingKHR或者vkCmdEndRendering
// Provided by VK_VERSION_1_3
void vkCmdBeginRendering(
    VkCommandBuffer                             commandBuffer,
    const VkRenderingInfo*                      pRenderingInfo);
// Provided by VK_KHR_dynamic_rendering
void vkCmdBeginRenderingKHR(
    VkCommandBuffer                             commandBuffer,
    const VkRenderingInfo*                      pRenderingInfo);
// Provided by VK_VERSION_1_3
void vkCmdEndRendering(
    VkCommandBuffer                             commandBuffer);
// Provided by VK_KHR_dynamic_rendering
void vkCmdEndRenderingKHR(
    VkCommandBuffer                             commandBuffer);

Dynamic Rendering的Pipeline

对于Dynamic Rendering,不需要提前创建FramebufferRenderPass,对于FramebufferRenderPass的指定是在vkCmdBeginRenderingKHR构建时指定的,由于不需要RenderPass了,所以需要Turbo提供不需要TRenderPassSubpass(Vulkan中对于Dynamic Rendering默认只支持一个Subpass的情况)的GraphicsPipeline版本。

对于提供不需要TRenderPassGraphicsPipeline版本,目前有两种选择

  1. 将当前TGraphicsPipeline提供一个没有TRenderPassSubpass的版本,用于适配支持Dynamic RenderingPipeline(在此基础上可以考虑从TGraphicsPipeline派生一个TRenderingPipeline)

  2. TPipeline派生出一个新的类TRenderingPipeline(内容和TGraphicsPipeline基本相同),专门用于适配支持Dynamic RenderingPipeline

根据Vulkan标准创建支持Dynamic Rendering需要在创建Graphics Pipeline时指定VkPipelineRenderingCreateInfoKHR(对于扩展)或者VkPipelineRenderingCreateInfo(对于标准),所以采用方式2中从TPipeline派生一个TRenderingPipeline比较符合一般直觉

//in Turbo::Core
class TRenderingPipeline : public Turbo::Core::TPipeline
{
  //大部分与TGraphicsPipeline一样
  //不需要指定RenderPass
  //不需要指定Subpass
};

创建Dynamic Rendering Pipeline时不需要指定RenderPassSubpass了,只需要指定要使用的附件Attachment的格式

// Provided by VK_VERSION_1_3
typedef struct VkPipelineRenderingCreateInfo {
  VkStructureType sType;
  const void* pNext;
  uint32_t viewMask;
  uint32_t colorAttachmentCount;
  const VkFormat* pColorAttachmentFormats;
  VkFormat depthAttachmentFormat;
  VkFormat stencilAttachmentFormat;
} VkPipelineRenderingCreateInfo;

对于Dynamic Rendering需要指定的附件Attachment仅仅为对应的VkFormat。为此声明一个TAttachmentsFormats用于记录各个附件的格式

//in Turbo::Core
class TAttachmentsFormats
{
  private:
    std::vector<TFormatType> colorAttachmentFormats;
    TFormatType depthAttachmentFormat = TFormatType::UNDEFINED;
    TFormatType stencilAttachmentFormat = TFormatType::UNDEFINED;

    void AddColorAttachmentFormat(TFormatType formatType);
    void SetDepthAttachmentFormat(TFormatType formatType);
    void SetStencilAttachmentFormat(TFormatType formatType);
};

Dynamic Rendering的渲染

对于Dynamic Rendering的渲染最基本的就两个调用函数:

// Provided by VK_VERSION_1_3
void vkCmdBeginRendering(
    VkCommandBuffer                             commandBuffer,
    const VkRenderingInfo*                      pRenderingInfo);
// Provided by VK_VERSION_1_3
void vkCmdEndRendering(
    VkCommandBuffer                             commandBuffer);

而对于vkCmdBeginRendering在渲染时需要指定VkRenderingInfo

// Provided by VK_VERSION_1_3
typedef struct VkRenderingInfo {
    VkStructureType                     sType;
    const void*                         pNext;
    VkRenderingFlags                    flags;
    VkRect2D                            renderArea;
    uint32_t                            layerCount;
    uint32_t                            viewMask;
    uint32_t                            colorAttachmentCount;
    const VkRenderingAttachmentInfo*    pColorAttachments;
    const VkRenderingAttachmentInfo*    pDepthAttachment;
    const VkRenderingAttachmentInfo*    pStencilAttachment;
} VkRenderingInfo;

默认情况下viewMask设置为0表示所有的附件的layer都按照layerCount的值来设置。最终要的是设置VkRenderingAttachmentInfo用于指定真正的附件。

// Provided by VK_VERSION_1_3
typedef struct VkRenderingAttachmentInfo {
    VkStructureType          sType;
    const void*              pNext;
    VkImageView              imageView;
    VkImageLayout            imageLayout;
    VkResolveModeFlagBits    resolveMode;
    VkImageView              resolveImageView;
    VkImageLayout            resolveImageLayout;
    VkAttachmentLoadOp       loadOp;
    VkAttachmentStoreOp      storeOp;
    VkClearValue             clearValue;
} VkRenderingAttachmentInfo;

为此声明一个TRenderingAttachments用于记录各个附件

//in Turbo::Core
class TRenderingAttachments
{
};

为了在Turbo中能够执行Dynamic Rendering指令,TCommandBufferBase中需要提供相应接口。

//in Turbo::Core::TCommandBufferBase
void CmdBeginRendering(const TRenderingAttachments& renderingAttachment);
//in Turbo::Core::TCommandBufferBase
void CmdEndRendering();

Tessellation

细分(Tessellation)。用于细分网格。

细分流程分为如下三个阶段:

  1. 细分控制着色器(可编程) Tessellation Control Shader
  2. 生成细分图元(不可编程) Tessellation Primitive Generator
  3. 细分评估(计算)着色器(可编程) Tessellation Evaluation Shader

Device Tessellation Feature

Vulkan中想要使用细分特性,需要查看和激活对应的设备feature。有关的细分的feature如下:

//in VkPhysicalDeviceFeatures
VkBool32    tessellationShader;
VkBool32    shaderTessellationAndGeometryPointSize;
  • 如果设备获取的特性中tessellationShaderVK_FALSE的话,说明该设备不支持细分特性。
  • shaderTessellationAndGeometryPointSize用于说明细分控制着色器和细分评估着色器和几何着色器中的内置变量PointSize是否有效可用,如果为VK_FALSE的话相关着色器禁止对其进行读写
//in VkPhysicalDeviceVulkan11Features 
VkBool32           multiviewTessellationShader;
  • multiviewTessellationShader用于说明一个RenderPass中的细分着色器是否支持multiview rendering,如果该特性为VK_FALSE的话,对于view mask非零的管线其不能包括任何细分着色器
//in VkPhysicalDeviceVulkan12Features 
VkBool32           shaderOutputLayer;
  • shaderOutputLayer indicates whether the implementation supports the ShaderLayer SPIR-V capability enabling variables decorated with the Layer built-in to be exported from vertex or tessellation evaluation shaders. If this feature is not enabled, the Layer built-in decoration must not be used on outputs in vertex or tessellation evaluation shaders.(没看懂干啥的,好像是顶点着色器和细分着评估色器中有个内置Layer声明,这个shaderOutputLayer好像是用来指示这个内置Layer声明能不能用)

还有一个特性需要明确一下:

//in VkPhysicalDeviceFeatures  
VkBool32           fillModeNonSolid;
  • fillModeNonSolid用于说明是否支持点point和线框wireframe的填充模式,如果该特性不支持的话VK_POLYGON_MODE_POINTVK_POLYGON_MODE_LINE是不能使用的(很多时候是将模型渲染成线框看细分结果)

Tessellation Graphics Pipeline

根据Vulkan标准,细分着色器是指定于:

//位于VkGraphicsPipelineCreateInfo中
const VkPipelineShaderStageCreateInfo* pStages;

对于Tessellation的配置是指定于:

//位于VkGraphicsPipelineCreateInfo中
const VkPipelineTessellationStateCreateInfo* pTessellationState;

根据Vulkan标准,有如下要求:

VUID-VkGraphicsPipelineCreateInfo-pStages-00729
If the pipeline is being created with pre-rasterization shader state and pStages includes a tessellation control shader stage, it must include a tessellation evaluation shader stage

如果VkGraphicsPipelineCreateInfo中的pStages中包括细分控制着色器话,VkGraphicsPipelineCreateInfo中的pStages必须也包含一个细分评估着色器

VUID-VkGraphicsPipelineCreateInfo-pStages-00730
If the pipeline is being created with pre-rasterization shader state and pStages includes a tessellation evaluation shader stage, it must include a tessellation control shader stage

如果VkGraphicsPipelineCreateInfo中的pStages中包括细分评估着色器话,VkGraphicsPipelineCreateInfo中的pStages必须也包含一个细分控制着色器

VUID-VkGraphicsPipelineCreateInfo-pStages-00731
If the pipeline is being created with pre-rasterization shader state and pStages includes a tessellation control shader stage and a tessellation evaluation shader stage, pTessellationState must be a valid pointer to a valid VkPipelineTessellationStateCreateInfostructure

如果VkGraphicsPipelineCreateInfo中的pStages中包括细分控制着色器和细分评估着色器的话,VkGraphicsPipelineCreateInfo中的pTessellationState必须是个有效的VkPipelineTessellationStateCreateInfo结构值

VUID-VkGraphicsPipelineCreateInfo-pStages-00736
If the pipeline is being created with pre-rasterization shader state and pStages includes tessellation shader stages, the topology member of pInputAssembly must be VK_PRIMITIVE_TOPOLOGY_PATCH_LIST

如果VkGraphicsPipelineCreateInfo中的pStages中包括细分着色器的话,VkGraphicsPipelineCreateInfo中的pInputAssemblyState中的topology成员值必须是VK_PRIMITIVE_TOPOLOGY_PATCH_LIST

VUID-VkGraphicsPipelineCreateInfo-topology-00737
If the pipeline is being created with pre-rasterization shader state and the topology member of pInputAssembly is VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, pStages must include tessellation shader stages

如果,VkGraphicsPipelineCreateInfo中的pInputAssemblyState中的topology成员值是VK_PRIMITIVE_TOPOLOGY_PATCH_LIST的话,VkGraphicsPipelineCreateInfo中的pStages中必须包括细分着色器

VUID-VkPipelineTessellationStateCreateInfo-patchControlPoints-01214
patchControlPoints must be greater than zero and less than or equal to VkPhysicalDeviceLimits::maxTessellationPatchSize

如果,VkGraphicsPipelineCreateInfo中的pTessellationState中的patchControlPoints成员变量必须大于0,并且小于等于VkPhysicalDeviceLimits::maxTessellationPatchSize

综上:

  • 如果想要使用细分特性,必须同时指定对应的细分控制着色器和细分评估着色器
  • 如果想要使用细分特性,VkGraphicsPipelineCreateInfo中的pTessellationState必须是个有效值
  • 如果想要使用细分特性,VkGraphicsPipelineCreateInfo中的pInputAssemblyState中的topology必须是VK_PRIMITIVE_TOPOLOGY_PATCH_LIST

Mesh Shader

Mesh ShaderVulkan中作为NVIDIA显卡上的一个设备扩展,被命名为VK_NV_mesh_shader。该扩展依赖于VK_KHR_get_physical_device_properties2扩展

有两种新的可编程着色器

  • Task Shader
  • Mesh Shader

这两个着色器可以用于替代如下过程

  • 获取顶点属性
  • 顶点着色器
  • 细分着色器
  • 几何着色器

同时该扩展同时会开启SPV_NV_mesh_shaderSPIR-V扩展

Vulkan中还有一个名为VK_EXT_mesh_shader的设备扩展,该扩展为更加通用的扩展。与VK_NV_mesh_shader不完全一样,有区别。

其中VK_NV_mesh_shader依赖VK_KHR_get_physical_device_properties2扩展

  • VK_KHR_get_physical_device_properties2Vulkan1.1标准中被提升为核心标准

其中VK_EXT_mesh_shader依赖VK_KHR_spirv_1_4扩展

  • VK_KHR_spirv_1_4Vulkan1.2标准中被提升为核心标准
  • VK_KHR_spirv_1_4依赖VK_KHR_shader_float_controls扩展
  • VK_KHR_shader_float_controlsVulkan1.2标准中被提升为核心标准
  • VK_KHR_shader_float_controls依赖VK_KHR_get_physical_device_properties2扩展

Mesh Shader Feature

需要查看设备是否支持Mesh Shader特性,之后再去激活相关特性。对于特性获取有两种方式:

  1. 通过Vulkan 1.1vkGetPhysicalDeviceFeatures2函数:

    // Provided by VK_VERSION_1_1
    void vkGetPhysicalDeviceFeatures2(
      VkPhysicalDevice physicalDevice,
      VkPhysicalDeviceFeatures2* pFeatures);
    
  2. 通过VK_KHR_get_physical_device_properties2扩展获得:

    // Provided by VK_KHR_get_physical_device_properties2
    void vkGetPhysicalDeviceFeatures2KHR(
      VkPhysicalDevice physicalDevice,
      VkPhysicalDeviceFeatures2* pFeatures);
    

Mesh Shader 接口函数

新增如下接口函数

// Provided by VK_NV_mesh_shader
void vkCmdDrawMeshTasksIndirectCountNV(
  VkCommandBuffer commandBuffer,
  VkBuffer buffer,
  VkDeviceSize offset,
  VkBuffer countBuffer,
  VkDeviceSize countBufferOffset,
  uint32_t maxDrawCount,
  uint32_t stride)
// Provided by VK_NV_mesh_shader
void vkCmdDrawMeshTasksIndirectNV(
  VkCommandBuffer commandBuffer,
  VkBuffer buffer,
  VkDeviceSize offset,
  uint32_t drawCount,
  uint32_t stride);
// Provided by VK_NV_mesh_shader
void vkCmdDrawMeshTasksNV(
  VkCommandBuffer commandBuffer,
  uint32_t taskCount,
  uint32_t firstTask);

主要看一下vkCmdDrawMeshTasksNV函数,其中

  • taskCount是设置本地工作组(local workgroup)在X轴处的数量,对于Y轴和Z轴其数量隐示默认为1
  • firstTaskX轴上的第一个工作组的ID

vkCmdDrawMeshTasksNV被调用时会有taskCount个本地工作组组成全局工作组

Mesh Shader GraphicsPipeline

当创建GraphicsPipeline图形管线时使用Mesh Shader有一些限值

  • 对于VkGraphicsPipelineCreateInfo::pVertexInputState如果使用了Mesh Shader的话这个参数将会被忽略

  • 对于VkGraphicsPipelineCreateInfo::pInputAssemblyState如果使用了Mesh Shader的话这个参数将会被忽略

  • 对于VkGraphicsPipelineCreateInfo::pStages中指定的Shader只能是一下两种组合不能混合使用:

    graph LR
    style id0 stroke-dasharray: 5 5
    MeshShader
    id0(TaskShader)-->MeshShader
    
    Loading
    graph LR
    style id0 stroke-dasharray: 5 5
    style id1 stroke-dasharray: 5 5
    style id2 stroke-dasharray: 5 5
    VertxShader
    
    FragmentShader
    VertxShader-->id0(TessellationControlShader)-->id1(TessellationEvaluationShader)-->id2(GeometryShader)-->FragmentShader
    
    Loading
  • 如果使用了Mesh Shader的话VkGraphicsPipelineCreateInfo::pDynamicStates中不能包含VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGYVK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDEVK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLEVK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXTVK_DYNAMIC_STATE_VERTEX_INPUT_EXT

  • 对于VkGraphicsPipelineCreateInfo::pTessellationState如果使用了Mesh Shader的话这个参数将会被忽略(Vulkan标准中没有明文指出该限值,只是说如果VkGraphicsPipelineCreateInfo::pStages有细分着色器的话此项目需要合法,通过Shader组合限制可推出该条目)

Specialization Constants

Specialization Constants用于在着色器中定义特化常量,这个特化的意思为在创建该着色器的Pipeline中可以指定该常量的值。

GLSL中声明如下:

layout(constant_id = 17) const int arraySize = 12;

根据GLSL标准:12为用户设置arraySize的默认值,17是用户自定义设置的特化常量id号。并且特化常量只能声明成标量数据,包括boolintuintfloatdouble类型。

GLSL内置的常量也可以声明成特化常量,比如:

layout(constant_id = 31) gl_MaxClipDistances; 

如果在该内置常量使用后再声明成特化常量的话会在编译时产生编译错误,也就是要求一个常量要么是非特化的普通常量,要么是特化常量,不能两个都是。

对于特化常量最常见的用法是通过使用local_size_{xyz}_id特化常量来分别指定计算着色器的gl_WorkGroupSize每个分量的id。比如:

layout(local_size_x_id = 18, local_size_z_id = 19) in;

这里没有设置local_size_y_id表明gl_WorkGroupSize.y是一个非特化常量值,也就是设定gl_WorkGroupSize为部分特化向量值。

对于xz的值可以滞后进行特化。在生成SPIR-V之后,使用id号分别为1819进行分别声明工作组纬度大小:

layout(local_size_x = 32, local_size_y = 32) in; // 大小为 (32,32,1)
layout(local_size_x_id = 18) in; // x的constant_id 
layout(local_size_z_id = 19) in; // z的constant_id

现有标准对于声明local_size_xlocal_size_ylocal_size_z并不会改变。对于相同的local-sizeid设置了不同的值或者在使用后再设置特化值都会产生编译错误。对于顺序、位置、声明和重复都不会导致错误

VkSpecializationInfo

Vulkan中通过创建Pipeline时设置VkPipelineShaderStageCreateInfo::pSpecializationInfo进行特化常量的数值设定。

// Provided by VK_VERSION_1_0
typedef struct VkPipelineShaderStageCreateInfo {
    VkStructureType                     sType;
    const void*                         pNext;
    VkPipelineShaderStageCreateFlags    flags;
    VkShaderStageFlagBits               stage;
    VkShaderModule                      module;
    const char*                         pName;
    const VkSpecializationInfo*         pSpecializationInfo;
} VkPipelineShaderStageCreateInfo;
// Provided by VK_VERSION_1_0
typedef struct VkSpecializationInfo {
    uint32_t                           mapEntryCount;
    const VkSpecializationMapEntry*    pMapEntries;
    size_t                             dataSize;
    const void*                        pData;
} VkSpecializationInfo;
// Provided by VK_VERSION_1_0
typedef struct VkSpecializationMapEntry {
    uint32_t    constantID;
    uint32_t    offset;
    size_t      size;
} VkSpecializationMapEntry;

其中Vulkan标准要求:VkSpecializationInfo中所有的VkSpecializationMapEntryconstantID不能重复 其中Vulkan标准要求:如果特化常量的类型为bool类型的话VkSpecializationMapEntry中的size大小必须是VkBool32的大小值

每一个Shader都会有自己的特化常量,对于特化常量的对外接口应该位于每个Shader中,大致流程如下:

TShader* some_shader = new TShader();
some_shader->SetConstant(0, true);
some_shader->SetConstant(1, 10);
some_shader->SetConstant(8, 20.0);
some_shader->SetConstant(13, 400);

之后在创建VkPipeline时根据指定的多个Shader中的特化常量进行数据设置。

  • 如果用户通过的TShader::SetConstant设置的idShader代码中并没有声明的话,不会造成任何问题,Turbo将会跳过该id(详情请查看后文的特化判断流程
  • 如果用户在Shader代码中已经声明了某个id的特化常量,而未通过的TShader::SetConstant设置特化常量值,则使用Shader代码中声明的默认值

特化判断流程

graph TD;
Start(("开始"))-->IsConstantEmpty{"用户设置的特化集是否为空"}
IsConstantEmpty--空-->Donothing("什么也不做")
IsConstantEmpty--非空-->GetIDAndValue("遍历特化集中每一个用户设置的特化常量对应ID和Value值")
GetIDAndValue-->IsSpecializationConstantsDeclaredInShader{"Shader中是否声明了相应的特化常量\n按照ID和Value的数值类型判断"}
IsSpecializationConstantsDeclaredInShader--未声明相应ID或类型对应不上-->Donothing
IsSpecializationConstantsDeclaredInShader--合法-->StatisticalCalculation("统计计算VkSpecializationInfo")
Loading

Ray Tracing

历史回顾

对于基于硬件的实时光追,最早是NVIDIA在2018年10月10日在DirectX 12中正式发布的名为DirectX Raytracing (DXR)的功能。而Khronos组织也在2018年的1月成立了Vulkan光追组制定Vulkan的光追标准,并在2020年3月发布了一个临时扩展(没记错的话应该是Vulkan 1.2,当时的扩展名为VK_KHR_ray_tracing,而VK_NV_ray_tracing扩展在这之前就已经发布了),经过一些用户和硬件供应商的反馈,最终在2020年的11月份发布了Vulkan实时光追标准的最终版(Vulkan 1.2.162)。Vulkan实时光追标准的发布是里程碑式的存在,标志着第一个工业级开源、跨平台、跨设备的光追加速标准问世(甚至可以在移动设备上使用光追加速)。

光追标准

最终发布版与临时发布版大体上没什么区别,最终发布版如下:

Vulkan的扩展标准:

SPIR-V的扩展标准:

GLSL的扩展标准:

扩展结构

最终发布版与临时发布版最显著的区别在于临时发布版的VK_KHR_ray_tracing扩展在最终发布版中被分成了三个扩展:

  • VK_KHR_acceleration_structure:用于加速结构的构建和管理
  • VK_KHR_ray_tracing_pipeline:用于光追着色器阶段和光追管线
  • VK_KHR_ray_query:用于所有着色器阶段的内部光线查询

Khronos标准组采纳了一些市场调研和厂家的意见,提供单独的光线查询功能而不需要创建光追管线,所以原先的VK_KHR_ray_tracing扩展被细分,用于避免重复和依赖。实现(设备制造商)可以实现VK_KHR_ray_tracing_pipeline或者VK_KHR_ray_query之一,或是两者都实现,取决于市场需求。两个扩展都依赖于VK_KHR_acceleration_structure扩展,该扩展用于提供基本的加速结构的管理。对于桌面级别的设备供应商任致力于都支持VK_KHR_ray_tracing_pipelineVK_KHR_ray_query扩展。

现在Vulkan光线追踪扩展标准由原先的临时提升到了核心中,也就是将光追扩展接口从vulkan_beta.h中移动到vulkan_core.h,所以用户不再需要声明#define VK_ENABLE_BETA_EXTENSIONS来激活Vulkan光线追踪的功能

这些扩展的依赖有些许变化,现在需要依赖Vulkan 1.1SPIR-V 1.4VK_KHR_acceleration_structure需要依赖Vulkan 1.1VK_EXT_descriptor_indexingVK_KHR_buffer_device_addressVK_KHR_deferred_host_operations。同样我们也意识到这么多依赖链很是繁琐,如果使用Vulkan 1.2事情将变得简单,但是并不是所有的平台都支持实现了Vulkan 1.2标准,并且我们并不想在光追标准中再增加其他的人为限值。我们也考虑到将VK_KHR_deferred_host_operations作为显示依赖是因为在创建管线时需要延迟操作需要该扩展。我们将VK_KHR_pipeline_library作为VK_KHR_ray_tracing_pipeline的一种相对松弛的扩展依赖而不是严格依赖,所以仅仅在使用相关扩展功能是才去激活扩展,用于减少负载。此外VK_KHR_acceleration_structureVK_KHR_ray_tracing_pipelineVK_KHR_ray_query都需要最小支持SPIR-V 1.4(该版本中增加了对于着色器的入口函数的改变),SPIR-V 1.5也可以在Vulkan 1.2上被使用。

功能方面,如下的标准需要所有的设备支持:

VK_KHR_acceleration_structure要求:

  • VK_KHR_deferred_host_operations
  • accelerationStructure
  • descriptorBindingAccelerationStructureUpdateAfterBind
  • descriptorIndexing有关的所有特性(如果支持Vulkan 1.2)或者使用VK_EXT_descriptor_indexing扩展
  • Vulkan 1.2bufferDeviceAddress或者VK_KHR_buffer_device_address扩展

设备如果支持VK_KHR_ray_tracing_pipeline要求:

  • VK_KHR_acceleration_structure
  • rayTracingPipeline
  • rayTracingPipelineTraceRaysIndirect
  • rayTraversalPrimitiveCulling(如果支持VK_KHR_ray_query
  • VK_KHR_pipeline_library

设备如果支持VK_KHR_ray_query要求:

  • VK_KHR_acceleration_structure
  • rayQuery

此外这些扩展有一些可选的功能。

对于VK_KHR_acceleration_structure要求:

  • accelerationStructureCaptureReplay
  • accelerationStructureIndirectBuild
  • accelerationStructureHostCommands

对于VK_KHR_ray_tracing_pipeline要求:

  • rayTracingPipelineShaderGroupHandleCaptureReplay
  • rayTracingPipelineShaderGroupHandleCaptureReplayMixed
  • rayTraversalPrimitiveCulling(如果不支持VK_KHR_ray_query)

加速结构(Acceleration Structures)

Vulkan对于光追扩展标准的最终版中变化最大的就是加速结构的创建和布局。

我们接纳了API转换层作者们的意见(比如vkd3d-proton),对于将DXR层置于Vulkan光追加速结构的顶层是不明智的。这使得加速结构的创建大小和存储在VkBuffer中发生了改变,而不是使用单独专用的加速结构进行存储。对应的变化位于VkAccelerationStructureKHRVkAccelerationStructureNV句柄不再是指代同一实例句柄,并且不能混用。与之相似的结构或者函数在使用这个两个句柄时也不再相同,也不能混用。

我们同时增加了加速结构类型声明VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR。可以用在当加速结构在创建时还不知道确切的加速结构类型(在顶部或是在底部),确切的加速结构类型必须是VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR或者VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,并且一旦创建就不能改变。开发Vulkan应用时不应该使用VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR,这会在将来影响设备功能和效率。

我们也接纳了Vulkan层实现作者们的意见(比如MoltenVK),意见指出一些光追要求(比如设备地址)使得Vulkan在层级上难以在其他API上实现。不幸的是对于像支持DXR的奇偶校验同样是不可能的。我们希望在未来的其他API版本中会支持这些特性。

我们听说开发者比较喜欢统一的创建接口和构造参数,比如同一VK_NV_ray_tracingDXR。我们将加速结构的创建更改成基于大小的创建,并且这个大小可与构造时的同一个结构体中计算出来(vkGetAccelerationStructureBuildSizesKHR),或者来自于压缩查询(vkCmdWriteAccelerationStructuresPropertiesKHR)。我们同时得知一些开发商需要在创建时得到更多信息,所以我们将pMaxPrimitiveCounts增加到了vkGetAccelerationStructureBuildSizesKHR中。

之前在几何描述某些方面是存在冲突的并且在自动代码生成方面不尽如人意(比如验证层(validation layers)),并且解决了由于ppGeometries的二元对立导致的歧义,并且增加了pGeometries,这要求二者在使用时只能指定其中一个使用。

其他的一些增加包括:加速结构的创建时间捕获和回溯标志位。nullDescriptor支持加速结构与VK_EXT_robustness2VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT相互作用。

最后,我们将所有的扩展更改成一致使用设备地址。这其中有很多命名建议,其中一些已被采纳,并且一些名称和命名风格被修改成统一并且可扩展的方式。有关更多细节请查阅VK_KHR_acceleration_structure的问题34的更新日志。

Host端的延迟操作(Deferred Host Operations)

注:Host端一般指CPU

我么改造了vkBuildAccelerationStructuresKHRvkCreateRayTracingPipelinesKHR指令的延迟Host端操作。取消了使用pNext对于各种独立创建和构造的链式操作,现在延迟操作通过指令的上层参数来设置指令是否为延迟操作。当指令为延迟操作的话,应用必须在延迟操作结束之后获取返回的数据。如果之前有一些延迟操作并且没有其他的措施防止不清晰的行为发生,这时难以明确合适能够获取安全数据。我们相信新的语义是清晰的并且对于并行友好,但是付诸的代价就是需要一直开启VK_KHR_deferred_host_operations扩展。

Acceleration-Structures

如上图为:Host延迟操作使用加速结构在多核CPU上部署已达到更高的帧率和限制帧停顿

光追管线

对于光追管线的改变并不多,基本上都是改变内部的SPIR-V和着色器编译链相关。

此外vkCreateRayTracingPipelinesKHR中增加了适用延迟操作的修改,并且将VkPipelineLibraryCreateInfoKHRVkRayTracingPipelineInterfaceCreateInfoKHR更改成可选项,这样如果VkPipelineLibraryCreateInfoKHRVkRayTracingPipelineInterfaceCreateInfoKHR没有使用就不须要激活VK_KHR_pipeline_library扩展了。

对于光追管线最大的改变就是明确的增加了栈大小的管理。光追管线在进行光线追踪时需要调用所有的有关执行链,这潜在会有大量的着色器集。当着色器执行时,驱动实现可能会使用栈去存储参数数据,这也间接要求栈要足够大,进而可以处理所有着色器的任何执行链的调用。默认的栈大小可能相当的大,所以我们给应用提供在管线编译之后使用更优栈大小的可能。一般当应用可以计算出紧凑型栈内存理应使用更优的策略。并且增加了一个新的动态状态VK_DYNAMIC_STATE_RAY_TRACING_PIPELINE_STACK_SIZE_KHR用于使用光追管线去查询着色器组vkGetRayTracingShaderGroupStackSizeKHR的栈大小时使用,与此对应的设置管线栈大小的函数即为vkCmdSetRayTracingPipelineStackSizeKHR

另一个新增特性来自于DXR层的积极反馈,即可以通过加速结构地址进行光线追踪。为此,加速结构的设备地址可通过vkGetAccelerationStructureDeviceAddressKHR将结果缓存在缓存中或者其他着色器资源中。对于着色器(SPIR-V),则可以使用OpConvertUToAccelerationStructureKHR显示转换声明OpTypeAccelerationStructureKHR描述符类型(在GLSL中使用accelerationStructureEXT构造)。其结果之后可以用于加速结构中使用OpTraceRayKHR指令进行追踪(traceRayEXT()),此种转换是单向的,并且没有其他操作支持加速结构描述符。

Ray-tracing-pipelines

如上图为:光追管线提供的隐式光线求交管理

SPIR-V标准组也对SPIR-V的扩展提供了反馈,将Payload参数更改成OpTraceRayKHR并且将Callable Data参数更改成OpExecuteCallableKHR。之前这些参数使用像GLSL中的location布局来进行匹配,然而这些locationSPIR-V没有意义,而是直接使用合适的存储类指针直接替换,为了实现这些需要新的指令操作码声明OpTraceRayKHROpExecuteCallableKHR。可以就与SPV_NV_ray_tracing进行划分防止混淆了。

另一个SPIR-V的改变来自于内部对于OpIgnoreIntersectionKHROpTerminateRayKHR的反馈,将这些转变成终止指令,因为他们会终止调用者的调用。这也必须是块中的最后一条指令。同样,这需要新的指令操作码。这对GLSL的冲击较大,这些函数指令将不再是内置函数,而是跳转语句,当在使用着色器时现在是仅仅使用ignoreIntersectionEXT而不是ignoreIntersectionEXT()

SPIR-V对于光追管线的的变化总结就是:提供了新功能和枚举RayTracingKHR,使得驱动实现和工具链在已过时的SPIR-V和最终版之间进行区分。并且对于还对ShaderRecordBufferKHR所需的显式布局进行了一些澄清,并将其视为StorageBuffer的存储类。同时我们也规定了返回值和对于OpReportIntersectionKHR的值T越界所返回的相关行为,并澄清一部分位域用于各种光追参数。

对于VK_KHR_acceleration_structure扩展,我们将设备缓存地址独立出来,这样就可以使用光线追踪指令通过VkStridedDeviceAddressRegionKHR去获取着色器绑定表中的缓存设备地址。vkCmdTraceRaysIndirectKHR也与之相似的,通过缓存设备地址间接获取参数。

我们也更新了Vulkan 1.2VK_KHR_vulkan_memory_model扩展之间的交互,并且要求一些内置变量成为Volatile变量,供着色器使用。

其他的改变包括增加创建2023年5月9日15:23:32捕获和着色器组句柄的回溯标志位,增加了一些之前忽略的属性和限值,和一些为了明确用途的重命名。有关更多细节请查阅VK_KHR_ray_tracing_pipeline的问题34SPV_KHR_ray_tracing的问题2还有扩展的更新日志。

光线查询

考虑到Vulkan接口对于表面光线查询欠缺,大部分的光线查询改变都在SPIR-V扩展和交互中。

SPV_KHR_ray_query也是支持通过加速结构地址进行发射光线查询,并且增加OpConvertUToAccelerationStructureKHR用于将加速结构设备地址转变成OpTypeAccelerationStructureKHR描述符。这些在之后可以通过加速结构使用OpRayQueryInitializeKHR进行追踪。

对于光追管线,也新增了功能和枚举RayQueryKHR,这可以是的驱动的实现者将老版的临时版本与最终版明确区分开。我们同时为提出遮罩使用一套位域值,并且不允许查询AABB图元的候选T值。

最终,我们对于光线参数进行了数量上的限值,要求HitT作为光线间隔用于OpRayQueryGenerateIntersectionKHR,并且限值追踪顶级的加速结构。

对于更多的细节和其他改变,请查阅VK_KHR_ray_query的问题1SPV_KHR_ray_query的问题1和扩展更新日志。

Acceleration-Structures

如上图为:光线查询提供从任意着色器中明确光线管理

正路在此

这一章将会给出创建加速结构新流程的纵览和对于资源创建与光追进行同步的快速入门。

加速结构的创建

注:加速结构的创建和构建是两个不同的东西,创建指的是创建加速结构句柄,构建指的是创建加速结构内部数据和结构

为了创建加速结构,应用必须首先确定加速结构需要的大小。对于创建时的加速结构、缓存大小和更新可通过vkGetAccelerationStructureBuildSizesKHR指令使用VkAccelerationStructureBuildSizesInfoKHR获得。对于创建加速结构时指定的shapetype位于VkAccelerationStructureBuildGeometryInfoKHR结构体中,该结构体之后也被用于真正的加速结构构建,但是此时加速结构的参数和几何数据并不需要全都填充完善(虽然可以填补完善),仅仅完善加速结构的类型、几何类型、数量和最大大小即可。这个大小可以支持任意足够相似的加速结构。对于加速结构目标会进行紧凑拷贝,这需要从vkCmdWriteAccelerationStructuresPropertiesKHR指令中获取大小。一旦需求的大小确定了,为加速结构创建VkBufferaccelerationStructureSize),并且一个或多个VkBuffer用于创建(buildScratchSize)和更新(updateScratchSize)缓冲。

之后,加速结构VkAccelerationStructureKHR对象就可以使用vkCreateAccelerationStructureKHR指令根据typesize创建,并将结果存放在VkAccelerationStructureCreateInfoKHR中指定的bufferoffset位置中。与Vulkan中的其他资源不同,指定的这一部分魂村将会完全用于加速结构,并不需要额外的缓存用于加速结构的查询或者内存绑定。如果你愿意,多个加速结构甚至可以放在同一个VkBuffer中,只需要多个加速结构之间不互相覆盖即可。

最后,使用vkCmdBuildAccelerationStructuresKHR指令可以用于去构建加速结构。此构建使用的是与创建时相同的VkAccelerationStructureBuildGeometryInfoKHR结构体,但是此时此时需要指定所有的几何数据(顶点,索引,变换,aabbs包围盒,和实例)和缓存数据。一旦构建完成此加速结构将会完全自完善(self-contained),并且构建的输入和缓存可以重复利用除非在之后的构建更新中有计划使用他们。

资源使用和同步

本章将提供各种缓存使用纵观,并且简单讲解光追操作有关的同步使用。

用于备份加速结构的缓存将会使用VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR进行创建。缓存将会使用VK_BUFFER_USAGE_STORAGE_BUFFER_BIT用于暂存空间,并且使用VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR用于加速结构的构建输入(比如顶点,索引,变换,aabbs包围盒,和实例)。如果缓存用于着色器绑定表将会使用VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR创建,并且如果使用间接构建的话追踪参数将会使用VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT创建使用。

为了与加速结构的构建指令(vkCmdBuildAccelerationStructuresKHRvkCmdBuildAccelerationStructuresIndirectKHR)进行同步,需要使用VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR管线阶段声明。访问加速结构的数据源或目标数据缓存将使用VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHRVK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR。构建时访问输入缓存(顶点,索引,变换,aabbs包围盒,和实例)使用VK_ACCESS_SHADER_READ_BIT访问类型并且访问间接参数使用VK_ACCESS_INDIRECT_COMMAND_READ_BIT访问类型。

为了与加速结构的拷贝指令(vkCmdWriteAccelerationStructuresPropertiesKHRvkCmdCopyAccelerationStructureKHRvkCmdCopyAccelerationStructureToMemoryKHRvkCmdCopyMemoryToAccelerationStructureKHR)进行同步,同样需要使用VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR管线阶段。访问加速结构的读写使用VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHRVK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR,此外通过设备地址访问的缓存使用VK_ACCESS_TRANSFER_READ_BITVK_ACCESS_TRANSFER_WRITE_BIT访问类型。

与光追指令(vkCmdTraceRaysKHRvkCmdTraceRaysIndirectKHR)进行同步,VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR管线阶段用于着色器访问绑定的缓存表使用VK_ACCESS_SHADER_READ_BIT访问,对于访问间接数据在VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT管线阶段使用VK_ACCESS_INDIRECT_COMMAND_READ_BIT进行访问。

与加速结构在任何图形,计算或是光追管线阶段进行光线查询同步,对应的管线阶段使用VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR进行数据访问。

Acceleration-Structures

如上图为:Vulkan的光追与DXR的比较。两者之间进行移植非常简单,包括可共享的HLSL光追着色器

结论和资源

现在最终版的Vulkan光追扩展已经发布,现在支持临时版本的嵌入式也会在不久支持最终版光追扩展,有关其他工具和组件可通过GitHub进行查阅。我们推荐所有的开发者转去使用最终的Khronos发布的Vulkan光追最终版。

支持Vulkan光追扩展的NVIDIA显卡驱动以及哪些显卡支持的信息可在developer.nvidia.com/vulkan-driver中找到。对于AMD显卡驱动可在https://www.amd.com/en/support/kb/release-notes/rn-rad-win-20-11-2-vrt-beta中找到。光追扩展同样可以通过2021年的IntelXe-HPG显卡支持,通过定期的驱动程序更新提供支持。

有关如何使用Vulkan光追用于混合渲染,同时使用光栅化和光追,以此来达到令人信服和画面同时保持交互程度请查看Vulkan Ray Tracing Best Practices for Hybrid Rendering博客讨论有关在Wolfenstein:Youngblood(德军总部:新血脉)中使用最终版光追扩展实现的反射效果。

也可以查阅现今进行更新后支持光追扩展的NVIDIA Vulkan Ray Tracing Tutorial和2020年6月份发布的NVIDIA Nsight Graphics developer tool。请关注更多即将发布的有关的生产驱动程序、工具和示例公告。

Vulkan工作组同样鼓励开发者和内容创建社区使用Vulkan的光追扩展并积极反馈问题。这可通过Khronos Developer SlackVulkan GitHub Issues Tracker进行讨论和反馈。

欢迎来到便携式、跨厂商、跨平台光线追踪加速的时代!