MiniEngine学习笔记 : CommandListManager

学习CommandListManager类

  • 前言
  • CommandQueue
    • [(1) 源码展示](#(1) 源码展示)
    • [(2) 源码分析](#(2) 源码分析)
    • [(3) 类使用分析](#(3) 类使用分析)
    • [(4) 类改进](#(4) 类改进)

前言

  • 书接上回CommandQueue,本篇文章分析CommandListManager。

CommandQueue

(1) 源码展示

  • 类头文件如下:
cpp 复制代码
#pragma once

#include "CommandQueue.h"

class CommandListManager
{
    friend class CommandContext;

public:
    CommandListManager();
    ~CommandListManager();

    void Create(ID3D12Device* pDevice);
    void Shutdown();

    CommandQueue& GetGraphicsQueue(void) { return m_GraphicsQueue; }
    CommandQueue& GetComputeQueue(void) { return m_ComputeQueue; }
    CommandQueue& GetCopyQueue(void) { return m_CopyQueue; }

    CommandQueue& GetQueue(D3D12_COMMAND_LIST_TYPE Type = D3D12_COMMAND_LIST_TYPE_DIRECT)
    {
        switch (Type)
        {
        case D3D12_COMMAND_LIST_TYPE_COMPUTE: return m_ComputeQueue;
        case D3D12_COMMAND_LIST_TYPE_COPY: return m_CopyQueue;
        default: return m_GraphicsQueue;
        }
    }

    ID3D12CommandQueue* GetCommandQueue()
    {
        return m_GraphicsQueue.GetCommandQueue();
    }

    void CreateNewCommandList(
        D3D12_COMMAND_LIST_TYPE Type,
        ID3D12GraphicsCommandList** List,
        ID3D12CommandAllocator** Allocator);

    // Test to see if a fence has already been reached
    bool IsFenceComplete(uint64_t FenceValue)
    {
        return GetQueue(D3D12_COMMAND_LIST_TYPE(FenceValue >> 56)).IsFenceComplete(FenceValue);
    }

    // The CPU will wait for a fence to reach a specified value
    void WaitForFence(uint64_t FenceValue);

    // The CPU will wait for all command queues to empty (so that the GPU is idle)
    void IdleGPU(void)
    {
        m_GraphicsQueue.WaitForIdle();
        m_ComputeQueue.WaitForIdle();
        m_CopyQueue.WaitForIdle();
    }

private:

    ID3D12Device* m_Device;

    CommandQueue m_GraphicsQueue;
    CommandQueue m_ComputeQueue;
    CommandQueue m_CopyQueue;
};
  • 类源文件如下:
cpp 复制代码
#include "pch.h"
#include "CommandListManager.h"

CommandListManager::CommandListManager() :
    m_Device(nullptr),
    m_GraphicsQueue(D3D12_COMMAND_LIST_TYPE_DIRECT),
    m_ComputeQueue(D3D12_COMMAND_LIST_TYPE_COMPUTE),
    m_CopyQueue(D3D12_COMMAND_LIST_TYPE_COPY)
{
}

CommandListManager::~CommandListManager()
{
    Shutdown();
}

void CommandListManager::Shutdown()
{
    m_GraphicsQueue.Shutdown();
    m_ComputeQueue.Shutdown();
    m_CopyQueue.Shutdown();
}

void CommandListManager::Create(ID3D12Device* pDevice)
{
    ASSERT(pDevice != nullptr);

    m_Device = pDevice;

    m_GraphicsQueue.Create(pDevice);
    m_ComputeQueue.Create(pDevice);
    m_CopyQueue.Create(pDevice);
}

void CommandListManager::CreateNewCommandList(D3D12_COMMAND_LIST_TYPE Type, ID3D12GraphicsCommandList** List, ID3D12CommandAllocator** Allocator)
{
    ASSERT(Type != D3D12_COMMAND_LIST_TYPE_BUNDLE, "Bundles are not yet supported");
    switch (Type)
    {
    case D3D12_COMMAND_LIST_TYPE_DIRECT: *Allocator = m_GraphicsQueue.RequestAllocator(); break;
    case D3D12_COMMAND_LIST_TYPE_BUNDLE: break;
    case D3D12_COMMAND_LIST_TYPE_COMPUTE: *Allocator = m_ComputeQueue.RequestAllocator(); break;
    case D3D12_COMMAND_LIST_TYPE_COPY: *Allocator = m_CopyQueue.RequestAllocator(); break;
    }

    ASSERT_SUCCEEDED(m_Device->CreateCommandList(1, Type, *Allocator, nullptr, MY_IID_PPV_ARGS(List)));
    (*List)->SetName(L"CommandList");
}

void CommandListManager::WaitForFence(uint64_t FenceValue)
{
    CommandQueue& Producer = Graphics::g_CommandManager.GetQueue((D3D12_COMMAND_LIST_TYPE)(FenceValue >> 56));
    Producer.WaitForFence(FenceValue);
}

(2) 源码分析

类成员变量如下:

cpp 复制代码
// 设备对象
ID3D12Device* m_Device;

// 图形、计算、拷贝命令队列
CommandQueue m_GraphicsQueue;
CommandQueue m_ComputeQueue;
CommandQueue m_CopyQueue;

类方法如下:

cpp 复制代码
// 构造函数,分别构造m_GraphicsQueue、m_ComputeQueue、m_CopyQueue
CommandListManager::CommandListManager() :
    m_Device(nullptr),
    m_GraphicsQueue(D3D12_COMMAND_LIST_TYPE_DIRECT),
    m_ComputeQueue(D3D12_COMMAND_LIST_TYPE_COMPUTE),
    m_CopyQueue(D3D12_COMMAND_LIST_TYPE_COPY)
{
}

// 析构时关闭
CommandListManager::~CommandListManager()
{
    Shutdown();
}

// 关闭,依次调用每个CommandQueue关闭
void CommandListManager::Shutdown()
{
    m_GraphicsQueue.Shutdown();
    m_ComputeQueue.Shutdown();
    m_CopyQueue.Shutdown();
}

// 创建,依次创建每个CommandQueue
void CommandListManager::Create(ID3D12Device* pDevice)
{
    ASSERT(pDevice != nullptr);

    m_Device = pDevice;

    m_GraphicsQueue.Create(pDevice);
    m_ComputeQueue.Create(pDevice);
    m_CopyQueue.Create(pDevice);
}

// 创建新的命令列表,传入命令类型,
// 剩余两个参数返回创建的命令列表和分配器。
void CommandListManager::CreateNewCommandList(D3D12_COMMAND_LIST_TYPE Type, ID3D12GraphicsCommandList** List, ID3D12CommandAllocator** Allocator)
{
    // 不支持D3D12_COMMAND_LIST_TYPE_BUNDLE
    ASSERT(Type != D3D12_COMMAND_LIST_TYPE_BUNDLE, "Bundles are not yet supported");

    // 根据类型,选择对应CommandQueue申请命令分配器
    switch (Type)
    {
    case D3D12_COMMAND_LIST_TYPE_DIRECT: *Allocator = m_GraphicsQueue.RequestAllocator(); break;
    case D3D12_COMMAND_LIST_TYPE_BUNDLE: break;
    case D3D12_COMMAND_LIST_TYPE_COMPUTE: *Allocator = m_ComputeQueue.RequestAllocator(); break;
    case D3D12_COMMAND_LIST_TYPE_COPY: *Allocator = m_CopyQueue.RequestAllocator(); break;
    }

    // 创建命令列表,返回到List中
    ASSERT_SUCCEEDED(m_Device->CreateCommandList(1, Type, *Allocator, nullptr, MY_IID_PPV_ARGS(List)));
    (*List)->SetName(L"CommandList");
}

// 等待围栏完成
void CommandListManager::WaitForFence(uint64_t FenceValue)
{
    /*
    * 这里通过全局CommandListManager变量-Graphics::g_CommandManager,
    * 调用GetQueue获取围栏对应的CommandQueue,然后调用CommandQueue::WaitForFence,
    * 我也不太理解明明此类就是CommandListManager,为什么还要定位到全局CommandListManager变量里去?
    * 总之这样不利于独立组件CommandListManager,我将改为使用此对象直接调用GetQueue。
    */
    CommandQueue& Producer = Graphics::g_CommandManager.GetQueue((D3D12_COMMAND_LIST_TYPE)(FenceValue >> 56));
    Producer.WaitForFence(FenceValue);
}

// 获取CommandQueue
CommandQueue& CommandListManager::GetGraphicsQueue(void) { return m_GraphicsQueue; }
CommandQueue& CommandListManager::GetComputeQueue(void) { return m_ComputeQueue; }
CommandQueue& CommandListManager::GetCopyQueue(void) { return m_CopyQueue; }

// 获取某类型CommandQueue
CommandQueue& CommandListManager::GetQueue(D3D12_COMMAND_LIST_TYPE Type = D3D12_COMMAND_LIST_TYPE_DIRECT)
{
    switch (Type)
    {
    case D3D12_COMMAND_LIST_TYPE_COMPUTE: return m_ComputeQueue;
    case D3D12_COMMAND_LIST_TYPE_COPY: return m_CopyQueue;
    default: return m_GraphicsQueue;
    }
}

// 获取图形CommandQueue
ID3D12CommandQueue* CommandListManager::GetCommandQueue()
{
    return m_GraphicsQueue.GetCommandQueue();
}

// 测试是否已到达围栏
bool CommandListManager::IsFenceComplete(uint64_t FenceValue)
{
    return GetQueue(D3D12_COMMAND_LIST_TYPE(FenceValue >> 56)).IsFenceComplete(FenceValue);
}

// CPU 将等待所有命令队列清空(以便 GPU 空闲)
void CommandListManager::IdleGPU(void)
{
    m_GraphicsQueue.WaitForIdle();
    m_ComputeQueue.WaitForIdle();
    m_CopyQueue.WaitForIdle();
}

(3) 类使用分析

  • 可以看到CommandListManager包含了图形、计算、拷贝CommandQueue,提供CreateNewCommandList可创建命令列表,可获取每个CommandQueue。这也体现了D3D12的设计理念,期望用户对每种类型使用统一命令队列。

(4) 类改进

  • WaitForFence方法依赖于全局变量实现,妨碍作为独立组件,主要就是修改此方法,改为使用自身查找CommandQueue实现。
  • 改进后CommandListManager类代码位于:CommandListManager,可作为独立组件使用。
相关推荐
whale fall19 小时前
celery -A tool.src.main worker --loglevel=info --queues=worker1_queue & 什么意思
python·学习·apache
EmbedLinX19 小时前
C++ 面向对象
开发语言·c++
weixin_4454023019 小时前
C++中的命令模式变体
开发语言·c++·算法
虚心低调的tom19 小时前
Moltbot 助手在 Windows 10 上安装并连接飞书教程
windows·飞书·moltbot
Hgfdsaqwr19 小时前
实时控制系统优化
开发语言·c++·算法
CSDN_RTKLIB19 小时前
Visual Studio不改变文件编码情况下解决C2001
c++·ide·visual studio
D_evil__19 小时前
【Effective Modern C++】第三章 转向现代C++:15. 尽可能使用constexpr
c++
2301_8213696119 小时前
嵌入式实时C++编程
开发语言·c++·算法
sjjhd65219 小时前
多核并行计算优化
开发语言·c++·算法
wotaifuzao20 小时前
【Keil 5安装】keil 5最新版本安装+环境配置+下载百度资源分享(安装包,注册机等)
stm32·单片机·嵌入式硬件·mcu·学习·keil5·最新keil