Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;如何设计命令队列_C++_Multithreading - Fatal编程技术网

C++ C++;如何设计命令队列

C++ C++;如何设计命令队列,c++,multithreading,C++,Multithreading,我有8个线程提供需要在GPU上执行的命令,我希望GPU通过其他8个线程填充的缓冲区在同一个线程上执行。我认为这将是相当简单,但我有问题的设计正确 struct CommandStruct { CComPtr<IDirect3DPixelShader9> Shader; const char* Param[9]; bool IsCompleted; }; concurrent_queue<CommandStruct*> Shader::cmdBuf

我有8个线程提供需要在GPU上执行的命令,我希望GPU通过其他8个线程填充的缓冲区在同一个线程上执行。我认为这将是相当简单,但我有问题的设计正确

struct CommandStruct {
    CComPtr<IDirect3DPixelShader9> Shader;
    const char* Param[9];
    bool IsCompleted;
};

concurrent_queue<CommandStruct*> Shader::cmdBuffer;
std::mutex Shader::startLock;
std::thread* Shader::WorkerThread = NULL;

void Shader::AddCommandToQueue(CommandStruct* cmd) {
    // Add command to queue.
    cmdBuffer.push(cmd);

    if (WorkerThread == NULL) {
        startLock.lock();
        if (WorkerThread == NULL) {
            WorkerThread = new std::thread(StartWorkerThread, env);
        }
        startLock.unlock();
    }
}
struct命令结构{
CComPtr着色器;
常量字符*参数[9];
布尔完成;
};
并发队列着色器::cmdBuffer;
std::mutex着色器::startock;
std::thread*Shader::WorkerThread=NULL;
void着色器::AddCommandToQueue(CommandStruct*cmd){
//将命令添加到队列。
cmdBuffer.push(cmd);
if(WorkerThread==NULL){
startock.lock();
if(WorkerThread==NULL){
WorkerThread=new std::thread(StartWorkerThread,env);
}
startock.unlock();
}
}
StartWorkerThread是一个函数,它获取cmdBuffer中的所有元素并逐个执行,直到缓冲区为空并在空闲几秒钟后停止

我遇到的问题是如何让调用方等待执行完成?起初我尝试了SetEvent,但性能非常差。然后我尝试使用Sleep(10)循环,但是它浪费了整个CPU

理想情况下,AddCommandToQueue将在命令完成后返回,但我也遇到了如何等待完成的相同问题


实现这种队列的正确方法是什么?它需要快速,因为它包含许多GPU指令。我尝试过举办一个活动,我尝试过在建筑物上挂一个IsCompleted标志,但设计还没有成功。

我意识到我晚了几年,但我想我应该提供一些想法。因为您正在查看命令系统,所以我会花时间确保您的命令是类型擦除的或抽象的。下面是一个可能对您有用的示例系统。你也可以看看他关于外部和内部参考的博客:

我之所以要删除或抽象您的命令系统类型,是为了维护某种层次的分层体系结构。以下是实时游戏引擎架构的典型示意图:

上图中需要注意的一点是,整个层专用于平台独立性。在这个级别之上,我将避免使用com指针之类的结构,因为它们在很大程度上依赖于平台。相反,考虑使用池或竞技场分配器创建一个资源管理系统,您可以在其中存储、缓存和重用资源、顶点缓冲区等。如果你迈出这一步,你就会成功

这也为您在创建命令系统时的成功做好了准备。在较高的层次上,可以将命令视为小型结构,其中包含数据管理器所包含数据的句柄。同样,通过这种方式,您不需要加载/重新加载VAO、VBO、IBO、着色器、材质等。如果您正确设置句柄系统,您将获得对数据管理器中相应数据的O(1)访问权限,并且您的命令将很小,这允许您对它们进行排序。在渲染绘制调用时,排序将变得非常重要,因为您希望在绘制较近的项目之前绘制较远的项目

如果遵循上述建议,则命令的内容应该是少量数据(仅限句柄/指针)和分派函数。下面是我自制GUI/游戏引擎中一些命令的具体示例:

struct make_viewport
{
    int width = platform::default_viewport_width;
    int height = platform::default_viewport_height;

    static auto dispatch(const void* data) {
        auto command = static_cast<const make_viewport*>(data);
        auto handle = platform::make_viewport(command->width, command->height);
        resource::manager::store(handle);
    };
};
分派函数的签名并不重要,但重要的是保持每个命令之间的签名一致,以便可以创建命令的多态数组。您将需要能够排序和执行这个多态数组,并且它的内容无法在编译时确定,因此您将无法使用类似std::tuple的东西(您可以使用std::variant,但您的数据局部性不会像它可能的那样好)

如果我必须提出建议,我会倾向于重载
operator()
为什么?如果您注意到,这将使任何命令都成为有效的函子。这允许与现代的C++技术(如LAMBDAS和标准算法)进行深度集成。换句话说,从上面重写make_viewport命令可以非常简单:

auto make_viewport = [width = platform::default_viewport_width, height = platform::default_viewport_height](const void* data)
{
    auto command = static_cast<const make_viewport*>(data);
    auto handle = platform::make_viewport(command->width, command->height);
    resource::manager::store(handle);
};
auto make_viewport=[width=platform::default_viewport_width,height=platform::default_viewport_height](const void*data)
{
自动命令=静态(数据);
自动处理=平台::生成视口(命令->宽度,命令->高度);
资源::管理器::存储(句柄);
};
当然,您需要找到一种处理多态性/排序/执行的方法,但是如果您找到了一种方法,lambda方法可能会非常有趣

如果您对处理类型擦除的方法感兴趣,以下是我的做法:

struct command_packet
{
    void* command = nullptr;
    dispatch_type dispatch = nullptr;
};

template<typename command_t> [[nodiscard]]
constexpr auto make_packet(command_t&& command) noexcept
{
    return command_packet
    {
        .command = &command,
        .dispatch = command.dispatch
    };
}
struct命令\u数据包
{
void*command=nullptr;
调度\类型调度=空PTR;
};
模板[[nodiscard]]
constexpr自动生成数据包(命令和命令)无异常
{
返回命令包
{
.command=&command,
.dispatch=命令.dispatch
};
}
您也可以使用一个虚拟基类来实现这一点,但我希望能够拥有POD命令,这样我就可以对命令的大小进行推理。然后我创建一个命令数组来执行每一帧。理想情况下,此阵列应具有线程本地存储,这使得很难从多个线程填充。但好消息是,您可以根据需要创建多个命令队列。例如,绘制调用总是需要按深度排序,因此需要在同一队列中。但是,如果您很聪明,可以选择使用基数排序对该队列进行排序。在实现基数排序时,可以将数据从多个线程排序到存储桶中,因为基数排序不需要任何比较操作。如果您的命令足够小,数量足够多,您将
struct command_packet
{
    void* command = nullptr;
    dispatch_type dispatch = nullptr;
};

template<typename command_t> [[nodiscard]]
constexpr auto make_packet(command_t&& command) noexcept
{
    return command_packet
    {
        .command = &command,
        .dispatch = command.dispatch
    };
}