vulkanscenegraph显示倾斜模型(6.4)-多线程下的记录与提交

前言

        上章深入分析了帧循环中呈现阶段的具体实现。本章将分析多线程下的记录与提交,进一步剖析vsg帧循环过程中的同步机制,并揭露信号量(VkSemaphore)和围栏(VkFence)以及vsg::FrameBlock与vsg::Barrier在其中的作用。


目录

  • 1 信号量(VkSemaphore)、栅栏(VkFence)、vsg::FrameBlock与vsg::Barrier
  • 2 多线程记录与提交

1 信号量(VkSemaphore)、围栏(VkFence)与vsg::FrameBlock、vsg::Barrier

        vsg::Semaphore封装了VkSaphore,用于将vulkan命令的完成与其他vulkan命令提交的开始同步,为GPU内部的同步;vsg::Fence封装了vkFence,用于同步Vulkan命令提交到队列的完成情况,用于应用程序(CPU端)与Vulkan命令提交到队列的完成情况(GPU端)的同步;vsg::FrameBlock提供了一种机制,用于同步等待新帧开始的线程;vsg::Barrier提供了一种同步多个线程的方法,一旦指定数量的线程加入Barrier,这些线程就会一起释放。

1.1 vsg::Semaphore

Semaphore::Semaphore(Device* device, VkPipelineStageFlags pipelineStageFlags, void* pNextCreateInfo) :
    _pipelineStageFlags(pipelineStageFlags),
    _device(device)
{
    VkSemaphoreCreateInfo semaphoreInfo = {};
    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    semaphoreInfo.pNext = pNextCreateInfo;

    VkResult result = vkCreateSemaphore(*device, &semaphoreInfo, _device->getAllocationCallbacks(), &_semaphore);
    if (result != VK_SUCCESS)
    {
        throw Exception{"Error: Failed to create semaphore.", result};
    }
}

        vsg::Semaphore构造函数使用vkCreateSemaphore创建信号量VkSemaphore,信号量的创建与某一逻辑设备绑定,即信号量可用于GPU内部同一队列或同一逻辑设备的不同队列间的同步。

Semaphore::~Semaphore()
{
    if (_semaphore)
    {
        vkDestroySemaphore(*_device, _semaphore, _device->getAllocationCallbacks());
    }
}

        vsg::Semaphore析构函数使用vkDestroySemaphore释放信号量VkSemaphore。

1.2 vsg::Fence

        vsg::Fence构造函数使用vkCreateFence创建VkFence,围栏的创建与某一逻辑设备绑定。

Fence::Fence(Device* device, VkFenceCreateFlags flags) :
    _device(device)
{
    VkFenceCreateInfo createFenceInfo = {};
    createFenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    createFenceInfo.flags = flags;
    createFenceInfo.pNext = nullptr;

    if (VkResult result = vkCreateFence(*device, &createFenceInfo, _device->getAllocationCallbacks(), &_vkFence); result != VK_SUCCESS)
    {
        throw Exception{"Error: Failed to create Fence.", result};
    }
}

         vsg::Fence析构函数使用vkDestroyFence释放围栏。

Fence::~Fence()
{
    if (_vkFence)
    {
        vkDestroyFence(*_device, _vkFence, _device->getAllocationCallbacks());
    }
}

       vsg::Fence在应用层(CPU端)的使用通过调用wait函数,其通过封装vkWaitForFences实现。

VkResult Fence::wait(uint64_t timeout) const
{
    return vkWaitForFences(*_device, 1, &_vkFence, VK_TRUE, timeout);
}

       vsg::Fence在GPU端使用时,需重置为无信号状态,否则可能会导致应用层调用vkWaitForFences卡死。

VkResult Fence::reset() const
{
    return vkResetFences(*_device, 1, &_vkFence);
}

     

1.3 vsg::FrameBlock

        vsg::FrameBlock提供了一种机制,用于同步等待新帧开始的线程。

        std::mutex _mutex;
        std::condition_variable _cv;
        ref_ptr<FrameStamp> _value;
        ref_ptr<ActivityStatus> _status;

        上述代码为vsg::FrameBlock的成员变量,其通过对std::mutex和std::condition_variable的封装实现了一种针对vsg::FrameStamp是否变化的阻塞能力,即同步所有等待新帧开始的线程,而变量_status(vsg::ActivityStatus类型)用于标记vsg::FrameBlock的阻塞能力是否有效。

        bool wait_for_change(ref_ptr<FrameStamp>& value)
        {
            std::unique_lock lock(_mutex);
            while (_value == value && _status->active())
            {
                _cv.wait(lock);
            }

            value = _value;
            return _status->active();
        }

        通过调用wait_for_change接口,当传入的vsg::FrameStamp对象与已有的一致时,阻塞应用程序所在线程。


                
### 实验6.4 Linux线程基础操作 #### 实验目标 通过本实验,学生可以掌握Linux环境下线程的基础概念及其基本操作方法。具体来说,学习如何创建、销毁线程,理解线程同步的重要性,并能够编写简单的多线程程序。 #### 实验环境准备 为了完成此实验,需确保开发环境中已安装GNU C Compiler (GCC),并具备标准C库支持POSIX线程接口pthread[^4]。此外,还需熟悉Linux终端命令行工具的使用。 #### 实验内容步骤说明 1. **线程创建** 使用`pthread_create()`函数来初始化一个新的线程。该函数原型如下所示: ```c int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); ``` 参数解释:第一个参数是指向线程标识符变量的指针;第二个参数用于指定属性对象(通常设为NULL表示默认属性);第三个参数是要执行的任务处理函数;第四个则是传递给任务函数的数据项地址。 2. **线程终止** 可以采用两种方式结束一个线程的生命期——正常退出或被取消。 - 正常情况下,当线程入口点返回时即自动消亡; - 如果希望提前停止某个正在运行中的线程,则可调用`pthread_cancel()`请求对其实施强制中断。 3. **线程同步机制** 在并发编程场景下,多个线程可能同时访问共享资源而导致竞争条件(race condition)发生。因此引入互斥锁(mutex lock)作为解决办法之一。下面展示了一个简单例子演示如何利用mutex保护临界区代码片段不被干扰: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER; void* print_message_function( void* ptr ); main(){ pthread_t thread1, thread2; char *message1 = "Thread 1"; char *message2 = "Thread 2"; /* Create independent threads each of which will execute function */ pthread_create(&thread1, NULL, print_message_function, (void*) message1); pthread_create(&thread2, NULL, print_message_function, (void*) message2); /* Wait till both threads complete before main continues */ pthread_join(thread1, NULL); pthread_join(thread2, NULL); exit(EXIT_SUCCESS); } void* print_message_function(void *ptr){ char *message; message = (char *) ptr; pthread_mutex_lock(&count_mutex); printf("%s \n", message); // Critical Section pthread_mutex_unlock(&count_mutex); pthread_exit(NULL); } ``` 上述实例中定义了一对互斥体(count_mutex)和条件变量(condition_var)[^4]。其中print_message_function代表各子线程要履行的具体职责逻辑,在进入打印语句之前先获取锁,从而避免两个独立工作单元交叉输出造成混乱局面。 #### 注意事项 - 编译含有pthread API的应用程序时记得附加-lpthread选项告知链接器加载相应的动态库文件[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CHPCWWHSU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值