首页 > 其他分享 >Vulkan中的同步与缓存控制

Vulkan中的同步与缓存控制

时间:2024-02-18 16:14:37浏览次数:24  
标签:缓存 Fence fence VK signaled 同步 submitInfo semaphore Vulkan

1.Introduction

Vulkan 提供显式的同步结构,允许 CPU 与 GPU 同步命令的执行。并且还可以控制 GPU 中命令的执行顺序。所有执行的 Vulkan 命令都将进入队列,并以某种未定义的顺序“不间断”执行。

有时,我们明确希望在执行新操作之前确保某些操作已完成。在编写vulkan应用时,虽然对给定 VkQueue 的操作是线性发生的,但如果我们有多个VkQueue,则无法保证顺序。为此,以及为了与 CPU 的通信,我们需要控制资源的访问同步。

对于同一个资源的访问之间的同步是vulkan应负责的内容之一,vulkan中一共提供了如下四种同步机制:

  1. Fence
  2. Semaphore
  3. Event
  4. Barrier

用以同步host/device之间,queues之间,queue submissions之间,以及一个单独的command buffer的commands之间的同步。

2.Fence

2.1Fence概述

首先我们介绍最简单的Fence。一句话总结,Fence提供了一种粗粒度的,从Device向Host单向传递信息的机制,即GPU -> CPU。

Host可以使用Fence来查询通过vkQueueSubmit/vkQueueBindSparse所提交的操作是否完成。简言之,在vkQueueSubmit/vkQueueBindSparse的时候,可以附加带上一个Fence对象。之后就可以使用这个对象来查询之前提交的状态了。

 

example:

VkResult vkQueueSubmit(
    VkQueue                                     queue,
    uint32_t                                    submitCount,
    const VkSubmitInfo*                         pSubmits,
    VkFence                                     fence);

其中,最后一个参数可以是一个有效的fence对象,当然,也可以指定为VK_NULL_HANDLE,标明不需要Fence。有趣的是,在vkQueueSubmit的时候,如果给定一个有效的fence对象,但是不提交任何信息,即submitCount为0,那么同样也可以算作一次成功的提交,等待之前所有提交到queue的任务都完成后,这个fence也就signaled了。这种使用方式提供了一种机制,可以让我们查询一个queue现在到底忙不忙(即提交后直接查询这个fence的状态,如果是signaled,证明不忙;如果unsignaled,证明之前提交的任务还没有完成)。

Fence本身只有两种状态,unsignaled或者signaled,大致可以认为fence是触发态还是未触发态。当使用vkCreateFence创建fence对象的时候,如果在标志位上填充了VkFenceCreateFlagBits的VK_FENCE_CREATE_SIGNALED_BIT,那么创建出来的fence就是signaled状态,否则都是unsignaled状态的。销毁一个fence对象需要使用vkDestroyFence。

伴随着vkQueueSubmit/vkQueueBindSparse一起提交的fence对象,可以使用vkGetFenceStatus来查询fence的状态。注意vkGetFenceStatus是非阻塞的,如果fence处于signaled状态,这个API返回VK_SUCCESS,否则,立即返回VK_NOT_READY。

当然,fence被触发到signaled状态,必须存在一种方法,将之转回到unsignaled状态,这个功能由vkResetFences完成,这个API一次可以将多个fence对象转到unsignaled状态。这个API结合VK_FENCE_CREATE_SIGNALED_BIT位,可以达到一种类似于C中do {} while;的效果,即loop的代码有着一致的表现:loop开始之前,所有的fence都创建位signaled状态,每次loop开始的时候,所用到的fence都由这个API转到unsignaled状态,伴随着submit提交过去。

等待一个fence,除了使用vkGetFenceStatus轮询之外,还有一个API vkWaitForFences提供了阻塞式地查询方法。这个API可以等待一组fence对象,直到其中至少一个,或者所有的fence都处于signaled状态,或者超时(时间限制由参数给出),才会返回。如果超时的时间设置为0,则这个API简单地看一下是否满足前两个条件,然后根据情况选择返回VK_SUCCESS,或者(虽然没有任何等待)VK_TIMEOUT。

简而言之,对于一个fence对象,Device会将其从unsignaled转到signaled状态,告诉Host一些工作已经完成。所以fence使用在Host/Device之间的,且是一种比较粗粒度的同步机制。

2.2.Fence实例

void executeQueue( VkCommandBuffer cmd )
{
    const VkCommandBuffer cmds[] = { cmd };
    VkFenceCreateInfo fenceInfo;
    VkFence drawFence;
    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fenceInfo.pNext = nullptr;
    fenceInfo.flags = 0;
    vkCreateFence( gDevice, &fenceInfo, nullptr, &drawFence );

    VkPipelineStageFlags pipeStageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    VkSubmitInfo submitInfo[1] = {};
    submitInfo[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo[0].pNext = nullptr;
    submitInfo[0].waitSemaphoreCount = 0;
    submitInfo[0].pWaitSemaphores = nullptr;
    submitInfo[0].pWaitDstStageMask = &pipeStageFlags;
    submitInfo[0].commandBufferCount = 1;
    submitInfo[0].pCommandBuffers = cmds;
    submitInfo[0].signalSemaphoreCount = 0;
    submitInfo[0].pSignalSemaphores = nullptr;

    HR( vkQueueSubmit( gQueue, 1, submitInfo, drawFence ) );

    VkResult res;
    do {
        res = vkWaitForFences( gDevice, 1, &drawFence, VK_TRUE, 100000000 );
    } while( res == VK_TIMEOUT );

    vkDestroyFence( gDevice, drawFence, nullptr );
}

 

 

3.Semaphore

3.1.VkSemaphore概述

VkSemaphore用以同步不同的queue之间,或者同一个queue不同的submission之间的执行顺序。即GPU -> GPU。

类似于fence,semaphore也有signaled和unsignaled的状态之分。然而由于在queue之间或者内部做同步都是device自己控制,所以一个semaphore的初始状态也就不重要了。所以,vkCreateSemaphore(3)就简单地不用任何额外参数创建一个semaphore对象,然后vkDestroySemaphore(3)可以用来销毁一个semaphore对象。不同于fence,没有重置或者等待semaphore的api,因为semaphore只对device有效。

在device上使用semaphore的最典型的场景,就是通过vkQueueSubmit提交command buffer时候,所需要的参数由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;

通过不同的参数搭配,可以达到如下效果:所提交的command buffer将在执行到每个semaphore等待阶段时候,检查并等待每个对应的wait semaphore数组中的semaphore是否被signal, 且等到command buffer执行完毕以后,将所有signal semaphore数组中的semaphore都signal起来。

通过这种方式,实际上提供了一种非常灵活的同步queue之间或者queue内部不同command buffer之间的方法,通过组合使用semaphore,AP可以显式地指明不同command buffer之间的资源依赖关系,从而可以让driver在遵守这个依赖关系的前提下,最大程度地并行化,以提高GPU的利用效率。

基本逻辑:

一些 Vulkan 操作(如 VkQueueSubmit)支持 Signal 或 Wait 信标。

如果将其设置为 Signal a semaphore,这意味着该操作将在执行时立即“锁定”该信标,并在执行完成后解锁。

如果将其设置为 Wait on a semaphore,则表示操作将等到该信标解锁后才开始执行。

3.2.VkSemaphore实例

伪代码:

VkSemaphore Task1Semaphore;
VkSemaphore Task2Semaphore;

VkOperationInfo OpAlphaInfo;
// Operation Alpha will signal the semaphore 1
OpAlphaInfo.signalSemaphore = Task1Semaphore;

VkDoSomething(OpAlphaInfo);

VkOperationInfo OpBetaInfo;

// Operation Beta signals semaphore 2, and waits on semaphore 1
OpBetaInfo.signalSemaphore = Task2Semaphore;
OpBetaInfo.waitSemaphore = Task1Semaphore;

VkDoSomething(OpBetaInfo);

VkOperationInfo OpGammaInfo;
//Operation gamma waits on semaphore 2
OpGammaInfo.waitSemaphore = Task2Semaphore;

VkDoSomething(OpGammaInfo);

 

参考链接

  • Vulkan Guide学习(1.5): https://zhuanlan.zhihu.com/p/451194569
  • vulkan中的同步和缓存控制之一,fence和semaphore: https://zhuanlan.zhihu.com/p/24817959
  • C++ (Cpp) vkCreateFence示例: https://cpp.hotexamples.com/zh/examples/-/-/vkCreateFence/cpp-vkcreatefence-function-examples.html

标签:缓存,Fence,fence,VK,signaled,同步,submitInfo,semaphore,Vulkan
From: https://www.cnblogs.com/ArsenalfanInECNU/p/18019451

相关文章

  • 分布式缓存应用:Memcache 或 Redis
    为什么要使用分布式缓存高并发环境下,例如典型的淘宝双11秒杀,几分钟内上亿的用户涌入淘宝,这个时候如果访问不加拦截,让大量的读写请求涌向数据库,由于磁盘的处理速度与内存显然不在一个量级,服务器马上就要宕机。缓存可以将经常读取的数据存储在快速的内存中,从而避免了频繁访问慢速......
  • Ubuntu18.04服务器局域网定时同步文件
    一、文件同步首先我们先了解一下rsync命令。rsync可以在本地系统之间或本地系统与远程系统之间同步、复制和备份文件和目录。rsync通过比较源与目标文件的差异来最小化数据传输,从而提高效率和速度。rsync命令有许多可选的参数,下面简单列一下常见的几个参数:-a:以归档模......
  • 通过注解实现本地缓存caffeine的学习
    注解源码如下1@Target(ElementType.METHOD)2@Retention(RetensionPolicy.RUNTIME)3@Documented4public@interfaceRvcCache{5Strngkey();6Stringid()defaultStringUtils.EMPTY;7}1@Component2@Aspect3@RequiredArgsConstructor4......
  • 通过`ssh`同步`tmux`剪贴板内容
    通过ssh同步tmux剪贴板内容通过ssh连接远程服务器时,可以通过xclip同步tmux剪贴板内容。这需要在服务器上安装xclip,且需要在ssh远程连接时开启X11。此处附tmux剪贴板调用xclip的配置:#CopythecurrentbuffertothesystemclipboardbindC-crun-b"tmuxsave-buffer-|x......
  • MySQL 主从数据库同步是如何实现的?
    回顾我们之前讲MySQL相关的几节课程,你会发现 主从同步有多重要:解决数据可靠性的问题需要用到主从同步;解决MySQL服务高可用要用到主从同步;应对高并发的时候,还是要用到主从同步。我们在运维MySQL集群时,遇到的很多常见的问题,比如说:为什么从节点故障会影响到主节点?为......
  • 【性能测试】MYSQL缓存命中率03
    一、查询缓存(querycache) 缓存命中率:所有的查询语句,命中缓存的请求数,占所有请求数的比例查看是否开启缓存命中率#缓存的开关showvariableslike'%query_cache_type%';#缓存的大小showvariableslike'%query_cache_size%';开启缓存设置MySQL的配置文件my......
  • dotnet aspnet redis 缓存 分布式缓存
    分布式缓存\appsettings.Development.json{"Logging":{"LogLevel":{"Default":"Information","Microsoft.AspNetCore":"Warning"}}}分布式缓存\appsettings.json{"Logg......
  • 力扣链表 哈希表 之 146. LRU 缓存
    请你设计并实现一个满足 LRU(最近最少使用)缓存约束的数据结构。实现LRUCache类:LRUCache(intcapacity)以正整数作为容量 capacity初始化LRU缓存intget(intkey)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。voidput(intkey,intvalue) ......
  • 一次打通FlinkCDC同步Mysql数据
    业务痛点离开了业务谈技术都是耍流氓。我们来聊聊基于业务的痛点,衍生出来的多种数据同步方案。业务中常见的需要数据同步的场景1、多个库的表合并到一张表。不同的业务线或者微服务在不同的数据库里开发,但是此时有些报表需要将多个库的类似的数据合并后做查询统计。或者,某些历......
  • 7.读写配置文件和添加缓存
    感觉没什么好总结的,直接上代码吧:配置文件:1添加一个枚举///<summary>///配置键名///</summary>publicenumConfigKey{///<summary>///系统配置///</summary>SystemConfig,///<summary>......